PcoWSkbVqDnWTu_dm2ix
We use cookies on this site to enhance your user experience
Collapse Sidebar

ProcessReceipt

MarketplaceService

ProductPurchaseDecision

After a player makes a purchase through a MarketplaceService/PromptProductPurchase|PromptProductPurchase() dialog, this callback is called multiple times until it returns Enum.ProductPurchaseDecision.PurchaseGranted. For example, the function is called again for a product when the player joins the game — or even after they have bought something else — unless you return Enum.ProductPurchaseDecision.PurchaseGranted.

It’s important to carefully examine the information passed to the callback via the receipt info table and properly process the receipt. See the code sample below for a model of how to create a receipt handling callback.

Receipt Info Table

The receipt info table passed to this callback will contain the following data:

KeyTypeDescription
PurchaseIdstringA unique identifier for the specific purchase.
PlayerIdnumberThe ID of the player who made the purchase.
ProductIdnumberThe ID of the purchased product.
CurrencySpentnumberThe amount of currency spent in the purchase.
CurrencyTypeenum/CurrencyType|CurrencyTypeThe type of currency spent in the purchase; always Enum.CurrencyType.Robux.
PlaceIdWherePurchasednumberThe ID of the place where the product was purchased (not necessarily the same as the current place's ID).

Parameters

Name Type Default Description

The receiptInfo is a table with the following keys:

  • PlayerId (number): The user ID of the player that made a purchase
  • PlaceIdWherePurchased (number): The place ID in which a purchase was made
  • PurchaseId (string): A unique identifier for a purchase that ought to be used to prevent granting a product multiple times
  • ProductId (number): The ID number of the purchased product
  • CurrencyType (Enum.CurrencyType): Deprecated. The type of currency used in the transaction.
  • CurrencySpent (number): The amount of currency spent in the transaction.

Expected Return Type

Return Type Summary

Code Samples


ProcessReceipt Callback

This code sample illustrates a MarketplaceService/ProcessReceipt|ProcessReceipt callback function for a game to handle purchasing of two Articles/Developer Products In Game Purchases|developer products (more can be added as needed). It properly checks for and records purchases using a GlobalDataStore called “PurchaseHistory.”

Most importantly, it properly returns Enum.ProductPurchaseDecision.PurchaseGranted when the transaction is successfully completed or if it’s detected that the purchase has already been granted using the “PurchaseHistory” data store.

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 = {}
-- ProductId 123123 for a full heal
productFunctions[123123] = function(receipt, player)
	-- Logic/code for player buying a full heal (may vary)
	if player.Character and player.Character:FindFirstChild("Humanoid") then
		-- Heal the player to full health
		player.Character.Humanoid.Health = player.Character.Humanoid.MaxHealth
		-- Indicate a successful purchase
		return true
	end
end
-- ProductId 456456 for 100 gold
productFunctions[456456] = function(receipt, player)
	-- Logic/code for player buying 100 gold (may vary)
	local stats = player:FindFirstChild("leaderstats")
	local gold = stats and stats:FindFirstChild("Gold")
	if gold then
		gold.Value = gold.Value + 100
		-- Indicate a successful purchase
		return true
	end
end

-- The core 'ProcessReceipt' callback function
local function processReceipt(receiptInfo)

	-- 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
	else
		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 = productFunctions[receiptInfo.ProductId]

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