Developer Product rewards granted twice after one purchase

I am trying to fix an issue where my developer product is rewarding twice after one purchase.



local CoinPurchasing = {
	[1300607177] = {45, 135, "Coin", "https://i.imgur.com/V9ayL8r.png"}, -- SMALL STACK OF COINS
	[1300607373] = {135, 405, "Coin", "https://i.imgur.com/y9UGleI.png"}, -- MEDIUM STACK OF COINS
	[1300607542] = {240, 720, "Coin", "https://i.imgur.com/x1KZuhL.png"}, -- BIG STACK OF COINS
	[1300607692] = {495, 1495, "Coin", "https://i.imgur.com/nRCkfDd.png"}, -- HUGE STACK OF COINS
	[1300608018] = {1020, 3060, "Coin", "https://i.imgur.com/vsY24Rm.png"}, -- CHEST OF COINS
	[1300608157] = {2595, 7785, "Coin", "https://i.imgur.com/kZTgXe0.png"}, -- GOLDEN CHEST OF COINS
	[1300608363] = {6000, 18000, "Coin", "https://i.imgur.com/YlfujIq.png"}, -- HOLY GRAIL
	[1837490830] = {nil, nil, "StarterKit", "https://i.imgur.com/aMVRtOX.png"}, -- STARTER KIT
}

game:GetService("MarketplaceService").PromptProductPurchaseFinished:Connect(function(UserId, DevProduct, Purchased)
	print(UserId, DevProduct, Purchased)
	if Purchased == false then
		local player = game.Players:GetPlayerByUserId(UserId)

		game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "UnsuccessfulPurchase")
	end
end)


game:GetService("MarketplaceService").ProcessReceipt = function(DevProduct)
	if CoinPurchasing[DevProduct.ProductId][3] == "Coin" then
		if game:GetService("ServerScriptService"):FindFirstChild("Data"):FindFirstChild("AllData"):FindFirstChild("TripleSale").Value == true then
			local player = game.Players:GetPlayerByUserId(DevProduct.PlayerId)
			game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value += CoinPurchasing[DevProduct.ProductId][2]
			game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "PurchaseCoins")
			game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "AddCoins", game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value)

			local s, PictureUrl = pcall(function()
				return game:GetService("HttpService"):JSONDecode(game:GetService("HttpService"):GetAsync("https://thumbnails.roproxy.com/v1/users/avatar-headshot?userIds="..DevProduct.PlayerId.."&size=420x420&format=Png"))
			end)
			if s and PictureUrl then
				PictureUrl=PictureUrl.data[1].imageUrl
			else
				PictureUrl="https://t3.rbxcdn.com/9fc30fe577bf95e045c9a3d4abaca05d" --link if can't get player headshot (default img)
			end

			CoinWebHook:post{
				username = "Coin Purchase (X3 SALE)",
				avatar_url = "https://i.imgur.com/NpJq2NW.png",
				content = "<@&1138860165064564793>",
				embeds = {{
					author = {
						name = player.Name,
						icon_url = PictureUrl,
						url = "https://www.roblox.com/users/"..DevProduct.PlayerId.."/profile"
					},
					title = game:GetService("MarketplaceService"):GetProductInfo(DevProduct.ProductId, Enum.InfoType.Product).Name,
					color = "13893632",
					thumbnail = {
						url = CoinPurchasing[DevProduct.ProductId][4],
					},
					description = ":dollar: Price: **R$ "..game:GetService("MarketplaceService"):GetProductInfo(DevProduct.ProductId, Enum.InfoType.Product).PriceInRobux.."** \n <:Coin3D:897964551688044565> Coins Purchased: **+"..CoinPurchasing[DevProduct.ProductId][2].."**\n\n:alarm_clock: Account Age: **"..player.AccountAge.."** \n :credit_card: UserID: **"..player.UserId.."**\n <:Coin3D:897964551688044565> Coins: **".. game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value.."**",
				}}
			}	
			return Enum.ProductPurchaseDecision.PurchaseGranted
		else
			print("Successful")
			local player = game.Players:GetPlayerByUserId(DevProduct.PlayerId)
			
			local OldCoins = game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value
				
			game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value += CoinPurchasing[DevProduct.ProductId][1]
			game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "PurchaseCoins")
			game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "AddCoins", game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value)

			local s, PictureUrl = pcall(function()
				return game:GetService("HttpService"):JSONDecode(game:GetService("HttpService"):GetAsync("https://thumbnails.roproxy.com/v1/users/avatar-headshot?userIds="..DevProduct.PlayerId.."&size=420x420&format=Png"))
			end)
			if s and PictureUrl then
				PictureUrl=PictureUrl.data[1].imageUrl
			else
				PictureUrl="https://t3.rbxcdn.com/9fc30fe577bf95e045c9a3d4abaca05d" --link if can't get player headshot (default img)
			end

			CoinWebHook:post{
				username = "Coin Purchase",
				avatar_url = "https://i.imgur.com/hK98riw.png",
				content = "<@&1138860165064564793>",
				embeds = {{
					author = {
						name = player.Name,
						icon_url = PictureUrl,
						url = "https://www.roblox.com/users/"..DevProduct.PlayerId.."/profile"
					},
					title = game:GetService("MarketplaceService"):GetProductInfo(DevProduct.ProductId, Enum.InfoType.Product).Name,
					color = "16441111",
					thumbnail = {
						url = CoinPurchasing[DevProduct.ProductId][4],
					},
					description = ":dollar: Price: **R$ "..game:GetService("MarketplaceService"):GetProductInfo(DevProduct.ProductId, Enum.InfoType.Product).PriceInRobux.."** \n <:Coin3D:897964551688044565>  Coins Purchased: **+"..CoinPurchasing[DevProduct.ProductId][1].."**\n\n:alarm_clock: Account Age: **"..player.AccountAge.."** \n :credit_card: UserID: **"..player.UserId.."**\n <:Coin3D:897964551688044565> Coins: **".. game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value.."**",
				}}
			}	
			
			return Enum.ProductPurchaseDecision.PurchaseGranted
		end
	elseif CoinPurchasing[DevProduct.ProductId][3] == "StarterKit" then
		local player = game.Players:GetPlayerByUserId(DevProduct.PlayerId)
		
		if game.ServerScriptService.Data.PlayerData.Weapons:FindFirstChild(player.Name) then
			if game.ServerScriptService.Data.PlayerData.Weapons:FindFirstChild(player.Name):FindFirstChild("Prototype") then
				game.ServerScriptService.Data.PlayerData.Weapons:FindFirstChild(player.Name):FindFirstChild("Prototype").Value = 1
			end
		end
		
		

		if game.ServerScriptService.Data.PlayerData.Boots:FindFirstChild(player.Name) then
			if game.ServerScriptService.Data.PlayerData.Boots:FindFirstChild(player.Name):FindFirstChild("NinjaTabi") then
				game.ServerScriptService.Data.PlayerData.Boots:FindFirstChild(player.Name):FindFirstChild("NinjaTabi").Value = true
			end
		end
		
		if game.ServerScriptService.Data.PlayerData.Capes:FindFirstChild(player.Name) then
			if game.ServerScriptService.Data.PlayerData.Capes:FindFirstChild(player.Name):FindFirstChild("BloodyDemon") then
				game.ServerScriptService.Data.PlayerData.Capes:FindFirstChild(player.Name):FindFirstChild("BloodyDemon").Value = true
			end
		end

		if game.ServerScriptService.Data.PlayerData.Coins:FindFirstChild(player.Name) then
			if game.ServerScriptService.Data.PlayerData.Coins:FindFirstChild(player.Name)then
				game.ServerScriptService.Data.PlayerData.Coins:FindFirstChild(player.Name).Value += 500
				
				game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "PurchaseCoins", "EquipStarterKit")
				game.ReplicatedStorage.Events.LocalEvent:FireClient(player, "AddCoins", game.ServerScriptService.Data.PlayerData.Coins:FindFirstChild(player.Name).Value)
			end
		end
		
		if game.ServerScriptService.Data.PlayerData.StarterKitTimer:FindFirstChild(player.Name) then
			game.ServerScriptService.Data.PlayerData.StarterKitTimer:FindFirstChild(player.Name).Value = os.time() - 90000
		end
		
		local s, PictureUrl = pcall(function()
			return game:GetService("HttpService"):JSONDecode(game:GetService("HttpService"):GetAsync("https://thumbnails.roproxy.com/v1/users/avatar-headshot?userIds="..DevProduct.PlayerId.."&size=420x420&format=Png"))
		end)
		if s and PictureUrl then
			PictureUrl=PictureUrl.data[1].imageUrl
		else
			PictureUrl="https://t3.rbxcdn.com/9fc30fe577bf95e045c9a3d4abaca05d" --link if can't get player headshot (default img)
		end

		MicroTransactionsWebHook:post{
			username = "MicroTransaction Purchase",
			content = "<@&1138860165064564793>",
			embeds = {{
				author = {
					name = player.Name,
					icon_url = PictureUrl,
					url = "https://www.roblox.com/users/"..DevProduct.PlayerId.."/profile"
				},
				title = game:GetService("MarketplaceService"):GetProductInfo(DevProduct.ProductId, Enum.InfoType.Product).Name,
				color = "21937",
				thumbnail = {
					url = CoinPurchasing[DevProduct.ProductId][4],
				},
				description = ":dollar: Price: **R$ "..game:GetService("MarketplaceService"):GetProductInfo(DevProduct.ProductId, Enum.InfoType.Product).PriceInRobux.."**\n\n:alarm_clock: Account Age: **"..player.AccountAge.."** \n :credit_card: UserID: **"..player.UserId.."**\n <:Coin3D:897964551688044565> Coins: **".. game.ServerScriptService.Data.PlayerData.Coins[player.Name].Value.."**",
			}}
		}	
		
		return Enum.ProductPurchaseDecision.PurchaseGranted
	end
end

I am using return Enum.ProductPurchaseDecision.PurchaseGranted but it seems like the server thinks it’s not granted

I believe this was an inteltional update to combat inflation.

No, this is not true. This doesn’t even make sense.

Is this only in studio?

is this asynchronous? otherwise the entire thread is yielding until it gets the network result.

meaning if you purchase it multiple times when previous process receipt hasn’t finished processing, it prompts it to try again

the other thing i can think of is that if neither of these conditions are being met, you arent returning anything at all


on a side note, this is very inneficient code, why dont you just store a single reference to this in a variable?

local findPlayer =
	game.ServerScriptService.Data.PlayerData.Coins:FindFirstChild(player.Name)

if findPlayer then
	findPlayer.Value += 500
end
--[[you even repeated the same if statement nested one inside another,
for no reason]]