MarketplaceService
MarketplaceService is responsible for in-experience transactions. The most notable methods are PromptProductPurchase and PromptPurchase, as well as the callback ProcessReceipt which must be defined so that transactions do not fail.
MarketplaceService also has methods that fetch information about developer products (GetProductInfo and GetDeveloperProductsAsync), passes (UserOwnsGamePassAsync), and other assets (PlayerOwnsAsset, PlayerOwnsBundle).
Understanding MarketplaceService is the first step towards learning to monetize an experience on Roblox, as well as DataStoreService which is responsible for saving and loading data related to purchases.
Summary
Properties
Methods
Prompts a user to purchase a bundle with the given bundleId.
Prompts a user to purchase a pass with the given gamePassId.
Prompts a user to purchase a developer product with the given productId.
Prompts a user to purchase an item with the given assetId.
Returns a Pages object which contains information for all of the current experience's developer products.
Returns the product information of an asset using its asset ID.
Returns whether the given player has the given asset.
Returns whether the given player owns the given bundle.
Returns true if the player with the given UserId owns the pass with the given gamePassId.
Events
Fires when a purchase prompt for a pass is closed.
Fires when the Roblox Premium purchase modal closes.
Fires when a purchase prompt closes for a developer product. You can use this event to detect when a purchase prompt is closed, but it should not be used to process purchases; instead use MarketplaceService.ProcessReceipt.
Fires when a purchase prompt for an affiliate gear sale or other asset is closed. Does not fire for developer product or pass prompts.
Callbacks
Properties
Methods
PromptBundlePurchase
Prompts a user to purchase a bundle with the given bundleId.
Parameters
Returns
PromptGamePassPurchase
Prompts a user to purchase a pass with the given gamePassId.
Parameters
Returns
PromptPremiumPurchase
Prompts a user to purchase Roblox Premium. To learn more about and incorporating Premium incentives into your experience, see Premium Payouts.
See Also
- MarketplaceService.PromptPremiumPurchaseFinished which fires when the Premium purchase UI closes.
- Players.PlayerMembershipChanged which fires when the server recognizes that a player's membership has changed.
Parameters
The player being prompted to purchase Premium.
Returns
Code Samples
local MarketplaceService = game:GetService("MarketplaceService")
local TeleportService = game:GetService("TeleportService")
local Players = game:GetService("Players")
local teleporter = script.Parent
local showPrompt = true
local PREMIUM_PLACE_ID = 012345678
local function onTeleporterTouch(otherPart)
local player = Players:GetPlayerFromCharacter(otherPart.Parent)
if not player then
return
end
-- If the user already has Premium, teleport them to the Premium-only place
if player.MembershipType == Enum.MembershipType.Premium then
TeleportService:TeleportAsync(PREMIUM_PLACE_ID, { player })
-- Else, prompt Premium upgrade (use debounce to show it only once every few seconds)
else
if showPrompt == false then
return
end
showPrompt = false
task.delay(5, function()
showPrompt = true
end)
MarketplaceService:PromptPremiumPurchase(player)
warn("Prompted Premium purchase")
end
end
teleporter.Touched:Connect(onTeleporterTouch)
-- If needed, use this event to know when the Premium modal is closed
local function onPromptFinished(player)
warn("Premium modal closed by", player)
end
MarketplaceService.PromptPremiumPurchaseFinished:Connect(onPromptFinished)
-- Handle potential Premium purchase from outside the game while user is playing
local function onMembershipChanged(player)
warn("Player membership changed; new membership is " .. tostring(player.MembershipType))
if player.MembershipType == Enum.MembershipType.Premium then
-- Teleport player to the Premium-only place
TeleportService:TeleportAsync(PREMIUM_PLACE_ID, { player })
end
end
Players.PlayerMembershipChanged:Connect(onMembershipChanged)
PromptProductPurchase
Prompts a user to purchase a developer product with the given productId.
Parameters
Returns
Code Samples
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local productId = 0000000 -- Change this to your developer product ID
-- Function to prompt purchase of the developer product
local function promptPurchase()
MarketplaceService:PromptProductPurchase(player, productId)
end
PromptPurchase
Prompts a user to purchase an item with the given assetId.
Parameters
Ignored.
Returns
Code Samples
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local promptPurchaseEvent = ReplicatedStorage:WaitForChild("PromptPurchaseEvent")
local part = Instance.new("Part")
part.Parent = workspace
local clickDetector = Instance.new("ClickDetector")
clickDetector.Parent = part
clickDetector.MouseClick:Connect(function()
promptPurchaseEvent:FireServer(16630147)
end)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local MarketplaceService = game:GetService("MarketplaceService")
local promptPurchaseEvent = Instance.new("RemoteEvent")
promptPurchaseEvent.Name = "PromptPurchaseEvent"
promptPurchaseEvent.Parent = ReplicatedStorage
-- Listen for the RemoteEvent to fire from a Client and then trigger the purchase prompt
promptPurchaseEvent.OnServerEvent:Connect(function(player, id)
MarketplaceService:PromptPurchase(player, id)
end)
GetDeveloperProductsAsync
Returns a Pages object which contains information for all of the current experience's developer products.
Returns
Code Samples
local MarketplaceService = game:GetService("MarketplaceService")
local developerProducts = MarketplaceService:GetDeveloperProductsAsync():GetCurrentPage()
for _, developerProduct in pairs(developerProducts) do
for field, value in pairs(developerProduct) do
print(field .. ": " .. value)
end
print(" ")
end
GetProductInfo
This method provides information about an asset, developer product, or pass given its asset ID and the InfoType. If no such item exists with the given ID, this method will throw an error. Information about the queried item is provided in a dictionary with the following keys. Note that not all information is provided or necessarily relevant for the kind of product you're querying.
Key | Type | Description |
---|---|---|
Name | string | The name shown on the asset's page. |
Description | string | The description as shown on the asset's page; can be nil if blank. |
PriceInRobux | number | The cost of purchasing the asset using Robux. |
Created | string | Timestamp of when the asset was created, for example 2022-01-02T10:30:45Z. Formatted using ISO 8601. |
Updated | string | Timestamp of when the asset was last updated by its creator, for example 2022-02-12T11:22:15Z. Formatted using ISO 8601. |
ContentRatingTypeId | number | Indicates whether the item is marked as 13+ in catalog. |
MinimumMembershipLevel | number | The minimum subscription level necessary to purchase the item. |
IsPublicDomain | boolean | Describes whether the asset can be taken for free. |
Creator Information
Key | Type | Description |
---|---|---|
Creator | table | Dictionary table of information describing the creator of the asset (see following lines). |
Creator.CreatorType | string | Either User or Group. |
Creator.CreatorTargetId | number | The ID of the creator user or group. |
Creator.HasVerifiedBadge | boolean | Whether the creator has a verified badge. |
Creator.Name | string | The name/username of the creator. |
Creator.Id | number | Use Creator.CreatorTargetId instead. |
Asset Information
Key | Type | Description |
---|---|---|
AssetId | number | If InfoType was Asset, the ID of the given asset. |
AssetTypeId | number | The type of asset. See AssetType for the asset type ID numbers. |
IsForSale | boolean | Describes whether the asset is purchasable. |
IsLimited | boolean | Describes whether the asset is a "limited item" that is no longer (if ever) sold. |
IsLimitedUnique | boolean | Describes whether the asset is a "limited unique" ("Limited U") item that only has a fixed number sold. |
IsNew | boolean | Describes whether the asset is marked as "new" in the catalog. |
Remaining | number | The remaining number of items a limited unique item may be sold. |
Sales | number | The number of items the asset has been sold. |
SaleAvailabilityLocations | table | The item's ProductLocationRestriction or sale location setting. |
CanBeSoldInThisGame | boolean | Describes whether the asset is purchasable in the current experience. |
Developer Products and Passes
Key | Type | Description |
---|---|---|
ProductId | number | If InfoType was Product, the product ID. |
IconImageAssetId | number | The asset ID of the product/pass icon, or 0 if there isn't one. |
Parameters
The asset ID of the specified product.
Returns
A dictionary containing information about the queried item, described in the previous tables.
Code Samples
local MarketplaceService = game:GetService("MarketplaceService")
local ASSET_ID = 125378389
local asset = MarketplaceService:GetProductInfo(ASSET_ID)
print(asset.Name .. " :: " .. asset.Description)
PlayerOwnsAsset
Returns whether the inventory of the given player contains an asset, given its ID. In the case that a query fails, this method will throw an error, so it's recommended to wrap calls to this method in pcall().
- This method should not be used for passes since they use a separate ID system. Legacy passes that still depend on an asset ID should use UserOwnsGamePassAsync() instead of this method.
- This method cannot be used to check for developer products since they can be purchased multiple times but not owned themselves. Instead, use a data store to save when a player has bought a developer product.
Parameters
A Player object whose inventory shall be tested for the ownership of the given asset.
The asset ID for which the given player's inventory shall be tested.
Returns
Indicates whether the given player's inventory contains the given asset.
Code Samples
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
-- The item we're checking for: https://www.roblox.com/catalog/30331986/Midnight-Shades
local ASSET_ID = 30331986
local ASSET_NAME = "Midnight Shades"
local function onPlayerAdded(player)
local _success, doesPlayerOwnAsset = pcall(MarketplaceService.PlayerOwnsAsset, MarketplaceService, player, ASSET_ID)
if doesPlayerOwnAsset then
print(player.Name .. " owns " .. ASSET_NAME)
else
print(player.Name .. " doesn't own " .. ASSET_NAME)
end
end
Players.PlayerAdded:Connect(onPlayerAdded)
PlayerOwnsBundle
Returns whether the inventory of given Player contains a bundle, given its ID. In the case that a query fails, this method will throw an error, so it's recommended to wrap calls to this method in pcall().
Parameters
A Player object whose inventory shall be tested for the ownership of the given bundle.
The bundle ID for which the given player's inventory shall be tested.
Returns
Indicates whether the given player's inventory contains the given bundle.
Code Samples
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")
-- The bundle we're checking for: https://www.roblox.com/bundles/589/Junkbot
local BUNDLE_ID = 589
local BUNDLE_NAME = "Junkbot"
Players.PlayerAdded:Connect(function (player)
local success, doesPlayerOwnBundle = pcall(function()
return MarketplaceService:PlayerOwnsBundle(player, BUNDLE_ID)
end)
if success == false then
print("PlayerOwnsBundle call failed: ", doesPlayerOwnBundle)
return
end
if doesPlayerOwnBundle then
print(player.Name .. " owns " .. BUNDLE_NAME)
else
print(player.Name .. " doesn't own " .. BUNDLE_NAME)
end
end)
UserOwnsGamePassAsync
Returns true if the player with the given UserId owns the pass with the given gamePassId, not to be confused with an asset ID.
Caching Behavior
Results of this function are remembered so that repeated calls will return quicker. This function will always return true if the player owns the pass upon first entering a server after having purchased the pass. If the pass is purchased in-experience through PromptGamePassPurchase(), this function may return false due to the caching behavior. Conversely, if the player deletes the pass from their inventory, this function may return true despite the player not owning the pass.
Parameters
Pass ID to check, not to be confused with an asset ID.
Returns
Events
PromptGamePassPurchaseFinished
This event fires when a purchase dialog for a pass is closed. It fires as the dialog closes, when the player presses "Cancel" at the prompt or "OK" at the success/error message.
To avoid duplicate item grants, store the purchase in a data store. If multiple attemps are made, check if the item has been already granted. For repeatable purchases use developer products.
See Also
- For developer product purchase prompts, connect to PromptProductPurchaseFinished.
- For affiliate gear sales or other assets, use PromptPurchaseFinished.
Parameters
The ID number of the pass shown in the prompt, not to be confused with an asset ID.
Whether the item was successfully purchased (false for errors and cancellations).
Code Samples
local MarketplaceService = game:GetService("MarketplaceService")
local function gamepassPurchaseFinished(...)
-- Print all the details of the prompt, for example:
-- PromptGamePassPurchaseFinished PlayerName 123456 false
print("PromptGamePassPurchaseFinished", ...)
end
MarketplaceService.PromptGamePassPurchaseFinished:Connect(gamepassPurchaseFinished)
PromptPremiumPurchaseFinished
This event fires when the Roblox Premium purchase modal closes, specifically when the player presses "Cancel" at the prompt, "OK" at the error message, or after the payment UI closes.
See Also
- PromptPremiumPurchase() to prompt a user to purchase Premium.
- Players.PlayerMembershipChanged which fires when the server recognizes that a player's membership has changed.
PromptProductPurchaseFinished
Fires when a purchase prompt closes for a developer product, right as the dialog closes when the player presses "Cancel" at the prompt or "OK" at the success/error message. You can use this event to detect when a purchase prompt is closed, but it should not be used to process purchases; instead use MarketplaceService.ProcessReceipt.
Unlike the similarly-named events above, this event fires with a Player.UserId and not a reference to the Player object.
Despite this event's deprecation, it is currently the only way to detect when a developer product's purchase dialog is closed.
- For affiliate gear sales or other assets, use MarketplaceService.PromptPurchaseFinished.
Parameters
The ID number of the developer product shown in the prompt (not to be confused with an asset ID).
Whether the item was successfully purchased (false for errors and cancellations).
PromptPurchaseFinished
Fires when a purchase prompt for an affiliate gear sale or other asset is closed. This fires right as the dialog closes, when the player presses "Cancel" at the prompt or "OK" at the success/error message. Does not fire for developer product or pass prompts.
See Also
- For pass prompts, use MarketplaceService.PromptGamePassPurchaseFinished.
- For developer product prompts, connect to MarketplaceService.PromptProductPurchaseFinished.
Parameters
The asset ID of the item shown in the prompt.
Whether the item was successfully purchased (false for errors and cancellations).
Code Samples
local MarketplaceService = game:GetService("MarketplaceService")
local function onPromptPurchaseFinished(player, assetId, isPurchased)
if isPurchased then
print(player.Name, "bought an item with AssetID:", assetId)
else
print(player.Name, "didn't buy an item with AssetID:", assetId)
end
end
MarketplaceService.PromptPurchaseFinished:Connect(onPromptPurchaseFinished)
PromptSubscriptionCancellationFinished
Parameters
Callbacks
ProcessReceipt
Parameters
Returns
Code Samples
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, result, errorMessage
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
-- Determine if the product was already granted by checking the data store
local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
local success, isPurchaseRecorded = pcall(function()
return purchaseHistoryStore:UpdateAsync(playerProductKey, function(alreadyPurchased)
if alreadyPurchased then
return true
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 nil
end
local handler = productFunctions[receiptInfo.ProductId]
local success, result = pcall(handler, receiptInfo, player)
-- If granting the product failed, do NOT record the purchase in datastores.
if not success or not result then
error("Failed to process a product purchase for ProductId: " .. tostring(receiptInfo.ProductId) .. " Player: " .. tostring(player) .. " Error: " .. tostring(result))
return nil
end
-- Record the transcation in purchaseHistoryStore.
return true
end)
end)
if not success then
error("Failed to process receipt due to data store error.")
return Enum.ProductPurchaseDecision.NotProcessedYet
elseif isPurchaseRecorded == nil then
-- Didn't update the value in data store.
return Enum.ProductPurchaseDecision.NotProcessedYet
else
-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
-- Set the callback; this can only be done once by one script on the server!
MarketplaceService.ProcessReceipt = processReceipt