GSmGpE6CwX2y9JjB25B8
We use cookies on this site to enhance your user experience

Handling Multiple Products

Handling Multiple Products

Aug 29 2019, 9:23 AM PST 5 min

When you start monetizing your game, you’ll probably end up using Articles/Developer Products In Game Purchases|Developer Products.

The moment you reach that stage, you’ll have to start thinking about using MarketplaceService/ProcessReceipt.

The example on MarketplaceService/ProcessReceipt only works for two developer products in a simple way.

Inserting the script several times, will result in overriding ProcessReceipt multiple times, each time making the previous one useless (If you have 5 scripts for 5 products, it’ll result in 4 being broken, and only the last one working).

Here is a documented example that uses a table, filled with productIds and functions to handle purchases.

This code is available as a model: [Wiki] ReceiptProcesser.

Code

local MarketplaceService = game:GetService("MarketplaceService")
local PurchaseHistory = game:GetService("DataStoreService"):GetDataStore("PurchaseHistory")

--[[
	This is how the table below has to be set up:
		[productId] = function(receipt,player) return allow end
			receipt is the receiptInfo, as called by ProcessReceipt
			player is the player that is doing the purchase
		If your function returns 'true', the purchase is approved.
		If your function doesn't return 'true', or errors, the purchase is cancelled.
--]]
local Products = {
	-- productId 1111 for a heal up
	[1111] = function(receipt,player)
		-- Check if we can actual heal him
		if not player.Character then return end
		local human = player.Character:findFirstChild("Humanoid")
		if not human then return end
		-- Heal him, and return true, indicating the purchase is done
		human.Health = human.MaxHealth
		return true
	end;
	-- productId 2222 for 100 gold
	[2222] = function(receipt,player)
		-- again, do checks, this time for the Gold value
		local stats = player:findFirstChild("leaderstats")
		local gold = stats and stats:findFirstChild("Gold")
		if not gold then return end -- no leaderstats, or "Gold" in them
		gold.Value = gold.Value + 100 -- give gold
		return true -- tell them of our success
	end;
	-- productId 3333 for 200 gold
	[3333] = function(receipt,player)
		-- same thing as above, but now for 200 gold
		local stats = player:findFirstChild("leaderstats")
		local gold = stats and stats:findFirstChild("Gold")
		if not gold then return end
		gold.Value = gold.Value + 200
		return true -- We can call this a success
	end;
	-- you can edit those above, or add your own
}

-- set MarketplaceService.ProcessReceipt to this function
-- (this is the same as doing: MarketplaceService.ProcessReceipt = function(recei... )
function MarketplaceService.ProcessReceipt(receiptInfo) 
    local playerProductKey = receiptInfo.PlayerId .. ":" .. receiptInfo.PurchaseId
    if PurchaseHistory:GetAsync(playerProductKey) then
        return Enum.ProductPurchaseDecision.PurchaseGranted --We already granted it.
    end
    -- find the player based on the PlayerId in receiptInfo
	local player = game:GetService("Players"):GetPlayerByUserId(receiptInfo.PlayerId)
	if not player then return Enum.ProductPurchaseDecision.NotProcessedYet end
	-- player left? don't process it

	local handler
	for productId,func in pairs(Products) do
		if productId == receiptInfo.ProductId then
			handler = func break -- found our handler
		end
	end
	
	-- apparently it's not our responsibility to handle this purchase
	-- if this happens, you should probably check your productIds etc
	-- let's just assume this is ment behavior, and let the purchase go through
	if not handler then return Enum.ProductPurchaseDecision.PurchaseGranted end
	
	-- call it safely with pcall, to catch any error
	local suc,err = pcall(handler,receiptInfo,player)
	if not suc then
		warn("An error occured while processing a product purchase")
		print("\t ProductId:",receiptInfo.ProductId)
		print("\t Player:",player)
		print("\t Error message:",err) -- log it to the output
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end
	
	-- if the function didn't error, 'err' will be whatever the function returned
	-- if our handler didn't return anything (or it returned false/nil), it means
	-- that the purchase failed for some reason, so we have to cancel it
	if not err then
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

    -- record the transaction in a Data Store
    suc,err = pcall(function()
        PurchaseHistory:SetAsync(playerProductKey, true)
    end)
    if not suc then
        print("An error occured while saving a product purchase")
		print("\t ProductId:",receiptInfo.ProductId)
		print("\t Player:",player)
		print("\t Error message:",err) -- log it to the output
		print("\t Handler worked fine, purchase granted") -- add a small note that the actual purchase has succeed
    end
    -- tell Roblox that we have successfully handled the transaction (required)
    return Enum.ProductPurchaseDecision.PurchaseGranted		
end
Tags:
  • products
  • script
  • monetization