There is bug where when player buys dev product and leaves game, then as soon as player joins game, game thinks player bought the dev product even though they didn’t. How do i fix this? This bug only happens in-game and not in Roblox studio so it’s hard to test it.
Code:
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local redEvent = ReplicatedStorage:WaitForChild("RedEvent")
local targetShadesEvent = ReplicatedStorage:WaitForChild("TargetShadesEvent")
local productPurchase1 = ReplicatedStorage:WaitForChild("DevProductPurchase1")
local productPurchase2 = ReplicatedStorage:WaitForChild("DevProductPurchase2")
local MarketplaceService = game:GetService("MarketplaceService")
local passId = 1729789798 -- set to your dev product id
local passId2 = 1730160326
local mps = game:GetService("MarketplaceService")
local ps = game:GetService("Players")
local function onPurchaseComplete(player, productId)
-- Check if this purchase has already been processed
-- If not, process the purchase
print(player.Name .. " has purchased the product.")
-- Depending on the product, fire the appropriate client event
if productId == passId then
print("e")
productPurchase1:FireClient(player)
elseif productId == passId2 then
print("er")
productPurchase2:FireClient(player)
else
print(productId)
end
end
local function onPurchaseComplete2(player)
print(player.Name .. " has purchased the product.")
productPurchase2:FireClient(player)
end
local function processReceipt(receiptInfo)
local player = ps:GetPlayerByUserId(receiptInfo.PlayerId)
if not player then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
if receiptInfo.ProductId == passId or receiptInfo.ProductId == passId2 then
local success, result = pcall(onPurchaseComplete, player, receiptInfo.ProductId)
if success then
return Enum.ProductPurchaseDecision.PurchaseGranted
else
warn("Error processing receipt:", result)
end
end
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local function redFunction(player)
mps:PromptProductPurchase(player, passId)
end
local function shadesOfTargetColor(player)
mps:PromptProductPurchase(player, passId2)
end
-- MarketplaceService.ProcessReceipt
function MarketplaceService.ProcessReceipt(receiptInfo)
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
if not player then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local productId = receiptInfo.ProductId
local amount = 0
if productId == passId2 or productId == passId then
processReceipt(receiptInfo)
return
end
return Enum.ProductPurchaseDecision.PurchaseGranted
end
redEvent.OnServerEvent:Connect(redFunction)
targetShadesEvent.OnServerEvent:Connect(shadesOfTargetColor)
Does this only apply to the same server or all servers? If it’s all servers, then you’re using a DataStore that’s causing that to happen. Studio doesn’t keep DataStores by default, but that can be enabled in the experience settings.
Then also make sure that the ID in Studio is your Roblox ID. If you’re using normal test mode, it should be. But if you’re using the server test mode, your IDs will be 0 or negative. Also I don’t believe that server test mode “players” will save data.
Word of warning: This also means that messing with your data in Studio, or anyone’s data for the matter, will impact their actual data in the live game.
The bug is still happening, even after updating the code:
local function onPurchaseComplete(player, productId)
-- Check if this purchase has already been processed
-- If not, process the purchase
print(player.Name .. " has purchased the product.")
-- Depending on the product, fire the appropriate client event
if productId == passId then
print("e")
productPurchase1:FireClient(player)
elseif productId == passId2 then
print("er")
productPurchase2:FireClient(player)
end
end
game.Players.PlayerAdded:Connect(function(player)
local function processReceipt(receiptInfo)
local productId = receiptInfo.ProductId
local quantity = ProductToQuantity[productId]
if quantity then
if productId == passId2 or productId == passId then
--processReceipt(receiptInfo)
onPurchaseComplete(player, productId)
return
end
else
local amount = 0
-- Determine the amount based on the product ID
for i, id in ipairs(donationIDs) do
if id == productId then
amount = determineAmount(i)
if amount >= 0 then
onDonationReceived(player, amount) -- Updated call
break
elseif amount >= 1 then
-- Handle server-specific logic if needed
end
end
end
if amount > 0 then
handlePurchase(player, amount)
end
end
return Enum.ProductPurchaseDecision.PurchaseGranted
end
game:GetService("MarketplaceService").ProcessReceipt = processReceipt
end)
On the documentation it says it’s possible for something to go wrong even when Enum.ProductPurchaseDecision.PurchaseGranted is returned, and therefore it can be called again. There is multiple issues with your script:
You should store the receipt info in a DataStore, and check if the receipt appears in the DataStore before granting the purchase (if it is, then return Enum.ProductPurchaseDecision.PurchaseGranted because it has already been granted).
Another important thing you’ve done wrong is set the process receipt callback in the Players.PlayerAdded event, which simply means whenever a player purchases a dev product, the player who last joined will recieve the grant, not the player who purchased the item.
What you should do is discard the code you’ve got so far and try again starting from the code shown in documentation code example.
EDIT AT 2024-01-17T09:07:00Z: Your original code would have actually worked except for this part here:
It is returning void instead of a purchase grant decision. Instead you should change it to this:
if productId == passId2 or productId == passId then
return processReceipt(receiptInfo)
end
However you should still save your purchase receipts to a data store and ensure it hasn’t already been processed.