ProcessReceipt
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
. Do not return PurchaseGranted if the product could not be granted.
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.
Script
. If you're selling multiple products in your game, this callback must handle receipts for all of them.
Receipt Info Table
The receipt info table passed to this callback will contain the following data:
Key | Type | Description |
---|---|---|
PurchaseId | string | A unique identifier for the specific purchase. |
PlayerId | number | The ID of the player who made the purchase. |
ProductId | number | The ID of the purchased product. |
CurrencySpent | number | The amount of currency spent in the purchase. |
CurrencyType | enum/CurrencyType|CurrencyType | The type of currency spent in the purchase; always Enum.CurrencyType.Robux . |
PlaceIdWherePurchased | number | The 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:
|
Expected return type:
Return Type | Summary |
---|---|
An enum representing in what manner the developer product receipt was processed:
|
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
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 = 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