Game passes and developer products are the two main ways to earn Robux from your players. Game passes are permanent one-time purchases—think VIP memberships or permanent perks. Developer products are consumable items that players can buy repeatedly, like power-ups or currency bundles. Both need to be created in the Creator Dashboard before you use them in scripts.
What's the Difference?
Game passes are one-time, permanent purchases. Once a player buys one, they own it forever. Perfect for VIP status, permanent stat boosts, or exclusive cosmetics.
Developer products are consumable. Players can buy them multiple times. Use these for temporary power-ups, in-game currency packs, respawns, or revival items.
Create a Game Pass
Game passes live in the Creator Dashboard, not in Studio itself.
- Go to create.roblox.com and sign in.
- Select your game from the Creations list.
- Click Monetization in the left sidebar.
- Click Game Passes.
- Click Create a Game Pass.
- Upload an icon (at least 512×512 pixels).
- Enter a name and description.
- Set the price in Robux (minimum 10, maximum 999).
- Click Create Game Pass.
Once created, note your game pass ID. You'll need it in your script.
Create a Developer Product
Developer products are also created in the Creator Dashboard.
- Go to create.roblox.com and sign in.
- Select your game.
- Click Monetization in the left sidebar.
- Click Developer Products.
- Click Create a Developer Product.
- Upload an icon (at least 512×512 pixels).
- Enter a name and description.
- Set the price in Robux.
- Click Create Developer Product.
Save the product ID for scripting.
Script Game Pass Purchases
Use MarketplaceService to handle game pass sales. This script goes in ServerScriptService.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
-- Your game pass ID (replace with your actual ID)
local GAME_PASS_ID = 12345678
-- Handle purchase completion
local function onGamePassPurchased(player, productId)
if productId == GAME_PASS_ID then
-- Give the player the perk
player.leaderstats.VIPStatus.Value = true
print(player.Name .. " purchased the VIP game pass!")
end
end
-- Listen for purchases
MarketplaceService.ProcessReceipt = function(receiptInfo)
local player = Players:FindFirstChild(tostring(receiptInfo.PlayerId))
if player then
onGamePassPurchased(player, receiptInfo.ProductId)
return Enum.ProductPurchaseDecision.PurchaseGranted
end
return Enum.ProductPurchaseDecision.NotProcessedYet
end
-- Optional: Check if a player owns the pass
function playerOwnsGamePass(player, gamePassId)
local success, result = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamePassId)
end)
return success and result or false
end
-- When a player joins, check if they own the pass
Players.PlayerAdded:Connect(function(player)
if playerOwnsGamePass(player, GAME_PASS_ID) then
print(player.Name .. " already owns the VIP pass")
onGamePassPurchased(player, GAME_PASS_ID)
end
end)Why this works:
ProcessReceiptfires whenever someone buys anything in your game.- We check the
ProductIdagainst our game pass ID. UserOwnsGamePassAsyncchecks if a player already owns it when they rejoin.- Return
PurchaseGrantedso the transaction completes;NotProcessedYetto retry later.
UserOwnsGamePassAsync in pcall() because it makes a web request. If the connection fails, you don't want your script to break.Script Developer Product Purchases
Developer products use the same MarketplaceService and ProcessReceipt system. This script also goes in ServerScriptService.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
-- Your developer product IDs (replace with your actual IDs)
local CURRENCY_PACK_100 = 87654321
local CURRENCY_PACK_500 = 87654322
local REVIVE_POTION = 87654323
-- Handle developer product purchases
local function onProductPurchased(player, productId)
if productId == CURRENCY_PACK_100 then
player.leaderstats.Currency.Value = player.leaderstats.Currency.Value + 100
print(player.Name .. " bought 100 currency")
elseif productId == CURRENCY_PACK_500 then
player.leaderstats.Currency.Value = player.leaderstats.Currency.Value + 500
print(player.Name .. " bought 500 currency")
elseif productId == REVIVE_POTION then
player.leaderstats.Revivals.Value = player.leaderstats.Revivals.Value + 1
print(player.Name .. " bought a revive potion")
end
end
-- Listen for all purchases
MarketplaceService.ProcessReceipt = function(receiptInfo)
local player = Players:FindFirstChild(tostring(receiptInfo.PlayerId))
if player then
onProductPurchased(player, receiptInfo.ProductId)
return Enum.ProductPurchaseDecision.PurchaseGranted
end
return Enum.ProductPurchaseDecision.NotProcessedYet
endKey points:
- Every purchase triggers
ProcessReceipt, whether it's a game pass or developer product. - Use
ifstatements to check theProductIdand give the right reward. - Developer products can be purchased repeatedly, so you just add to the player's value.
- Return
PurchaseGrantedimmediately to complete the sale.
Prompt a Purchase in Game
Use a LocalScript in StarterPlayer > StarterCharacterScripts or StarterPlayer > StarterPlayerScripts to let players buy items with a button click.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local GAME_PASS_ID = 12345678
-- Create a simple GUI button
local screenGui = Instance.new("ScreenGui")
screenGui.ResetOnSpawn = false
screenGui.Parent = player:WaitForChild("PlayerGui")
local buyButton = Instance.new("TextButton")
buyButton.Name = "BuyVIPButton"
buyButton.Size = UDim2.new(0, 150, 0, 50)
buyButton.Position = UDim2.new(0.5, -75, 0, 10)
buyButton.Text = "Buy VIP Pass"
buyButton.Parent = screenGui
-- Handle button click
buyButton.MouseButton1Click:Connect(function()
MarketplaceService:PromptGamePassPurchase(player, GAME_PASS_ID)
end)For developer products, use a different method:
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local CURRENCY_PACK_ID = 87654321
local screenGui = Instance.new("ScreenGui")
screenGui.ResetOnSpawn = false
screenGui.Parent = player:WaitForChild("PlayerGui")
local buyButton = Instance.new("TextButton")
buyButton.Name = "BuyCurrencyButton"
buyButton.Size = UDim2.new(0, 150, 0, 50)
buyButton.Position = UDim2.new(0.5, -75, 0, 70)
buyButton.Text = "Buy Currency"
buyButton.Parent = screenGui
buyButton.MouseButton1Click:Connect(function()
MarketplaceService:PromptProductPurchase(player, CURRENCY_PACK_ID)
end)The key difference: PromptGamePassPurchase for game passes, PromptProductPurchase for developer products. Both open Roblox's official purchase dialog—you never handle payment directly.
Check Ownership Before Granting Access
When a player joins or requests a perk, check if they own the game pass first. This prevents exploiting and handles rejoin scenarios.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local GAME_PASS_ID = 12345678
local function grantVIPPerks(player)
-- Give the player their VIP benefits
player.leaderstats.VIPStatus.Value = true
print(player.Name .. " has VIP access")
end
Players.PlayerAdded:Connect(function(player)
local success, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, GAME_PASS_ID)
end)
if success and owns then
grantVIPPerks(player)
elseif not success then
print("Error checking game pass for " .. player.Name)
end
end)Run this whenever a player joins. If they own the pass, activate their benefits immediately without waiting for a purchase event.
Best Practices
- Use leaderstats or tags: Track purchase status with values players can see, so they know what they own.
- Wrap async calls in pcall(): Web requests can fail. Always catch errors.
- Test thoroughly: Use test accounts before publishing to ensure purchases grant the right rewards.
- Return PurchaseGranted immediately: Don't wait for other systems. Once the script says the purchase succeeded, Roblox bills the player.
- Price appropriately: Game passes range 10–999 Robux. Developer products have no upper limit, but high prices deter purchases.
- Make purchases optional: Never lock core gameplay behind a paywall. Perks should enhance, not restrict.
Common Issues
ProcessReceipt never fires: Check that your game is published and you're testing with a real account, not in Studio solo mode. Purchases don't work in test mode.
Player loses perks on rejoin: You need an ownership check when they join (see "Check Ownership Before Granting Access" above). Don't rely only on ProcessReceipt.
Script error: "UserOwnsGamePassAsync is not a valid member": Use the exact spelling and capitalization. Always wrap it in pcall().
Button click doesn't prompt purchase: Make sure you're using a LocalScript in the player's PlayerGui or StarterPlayer. Server scripts can't show purchase dialogs.