How to Make a Gamepass in Roblox (GamePass & Developer Products)

Last updated:

Quick answer: Create the pass in the Creator Dashboard, prompt the purchase with MarketplaceService:PromptGamePassPurchase from a LocalScript, and grant the perk only when the server confirms it with UserOwnsGamePassAsync — Developer Products are the consumable alternative and must be granted inside MarketplaceService.ProcessReceipt.

A Roblox gamepass script has three separate jobs: creating the pass in the Creator Dashboard, prompting the purchase from a client script with MarketplaceService:PromptGamePassPurchase, and checking or granting the perk on the server with UserOwnsGamePassAsync. Developer Products follow the same creation flow but are granted exclusively through MarketplaceService.ProcessReceipt, which is the only receipt pattern Roblox guarantees against double-charging.

What's the difference between a gamepass and a Developer Product?

Both are catalog items you attach to your experience and both are bought with Robux, but they behave completely differently:

GamePassDeveloper Product
PurchasesOne per account, foreverUnlimited, repeatable
Typical perkVIP tag, 2x walk speed, private island100 coins, health potion, XP boost
DetectionUserOwnsGamePassAsyncProcessReceipt callback
Survives rejoinYes, ownership is permanentYou persist the granted effect yourself

A gamepass is a permanent unlock: once the account owns it, every future server can ask "does this player own it?" and get yes. A Developer Product is consumable — the player buys it again every time they want the effect, and it is your job to record what was granted (usually in a DataStore).

Rule of thumb: if the player should have it forever, it is a gamepass. If they should be able to buy it again tomorrow, it is a Developer Product.

How do I create a gamepass in the Creator Dashboard?

Open the Creator Dashboard, select your experience, then Monetization → Passes → Create a pass. Give it a name, icon and description, and set a price in Robux — a pass with no price stays Off Sale and cannot be prompted. Developer Products live under Monetization → Developer Products and additionally ask for a price per purchase.

After saving, copy the numeric ID of the item (it is in the page URL, /store/1234567890/...). That ID is what your scripts use — not the name. Keep them in one config ModuleScript so a price or product change is a one-line edit:

-- ModuleScript: ReplicatedStorage/GamepassConfig
return {
	VIP_PASS_ID = 1234567890,
	TWO_X_SPEED_PASS_ID = 1234567891,
	COINS_100_PRODUCT_ID = 1234567892,
}

Config in ReplicatedStorage is readable by both the server and the client, so the LocalScript that prompts the purchase and the server script that validates it use the same IDs.

How do I prompt a gamepass purchase from a GUI button?

Purchase prompts must be triggered from the client — a server script calling PromptGamePassPurchase will silently do nothing useful. Put a LocalScript next to your TextButton (or in StarterPlayerScripts looking the button up) and prompt on click:

-- LocalScript inside a TextButton
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local config = require(ReplicatedStorage:WaitForChild("GamepassConfig"))
local button = script.Parent

button.Activated:Connect(function()
	local player = game:GetService("Players").LocalPlayer
	MarketplaceService:PromptGamePassPurchase(player, config.VIP_PASS_ID)
end)

This opens Roblox's own purchase dialog. Never build your own Robux transfer flow and never trust a client message that says "I bought it" — the dialog is just the request; the confirmation is what the server verifies.

How do I know when the purchase finished?

MarketplaceService.PromptGamePassPurchaseFinished fires when the dialog closes, on both the client and the server, with the player, the pass ID, and a wasPurchased boolean. Handle it on the server, because that is where the perk lives:

-- Server Script in ServerScriptService
local MarketplaceService = game:GetService("MarketplaceService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local config = require(ReplicatedStorage:WaitForChild("GamepassConfig"))
local ownedPasses = {} -- [player] = { [passId] = true }

local function grantPass(player, passId)
	ownedPasses[player] = ownedPasses[player] or {}
	ownedPasses[player][passId] = true
	if passId == config.TWO_X_SPEED_PASS_ID then
		local humanoid = player.Character and player.Character:FindFirstChildOfClass("Humanoid")
		if humanoid then
			humanoid.WalkSpeed = 32
		end
	end
end

MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, wasPurchased)
	if wasPurchased then
		grantPass(player, passId)
	end
end)

Handling the finished prompt means the buyer gets the perk immediately, without leaving and rejoining. The ownership check in the next section is still what protects you on every later join.

How do I check gamepass ownership on the server?

MarketplaceService:UserOwnsGamePassAsync(userId, passId) is the authoritative answer, and it is a web request, so wrap it in pcall. Run it when the player joins and cache the result — checking ownership on every game tick spams Roblox's API for nothing:

local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local config = require(ReplicatedStorage:WaitForChild("GamepassConfig"))
local ownedPasses = {} -- [player] = { [passId] = true }

local function checkOwnership(player)
	ownedPasses[player] = {}
	for _, passId in { config.VIP_PASS_ID, config.TWO_X_SPEED_PASS_ID } do
		local ok, owns = pcall(function()
			return MarketplaceService:UserOwnsGamePassAsync(player.UserId, passId)
		end)
		if ok and owns then
			ownedPasses[player][passId] = true
		elseif not ok then
			warn("Ownership check failed for " .. player.Name .. ": " .. tostring(owns))
		end
	end
end

Players.PlayerAdded:Connect(function(player)
	checkOwnership(player)
	-- apply the cached perks to the character here
end)

Any expensive perk — damage multipliers, area access, sell prices — must read from this server-side cache, never from a client claim. If a RemoteEvent message could grant a perk, an exploiter can fire that message directly.

Where do I handle Developer Product receipts?

Developer products have no "does the player own it" question — every purchase is an event, and the delivery point is MarketplaceService.ProcessReceipt. This callback is the only safe receipt pattern in Roblox: connect it exactly once, from exactly one server Script, and return a decision:

local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local config = require(ReplicatedStorage:WaitForChild("GamepassConfig"))

local productActions = {
	[config.COINS_100_PRODUCT_ID] = function(player)
		-- add 100 coins to the player's server-side balance
	end,
}

MarketplaceService.ProcessReceipt = function(receiptInfo)
	local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
	if not player then
		-- the buyer left the server: ask Roblox to retry later
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

	local action = productActions[receiptInfo.ProductId]
	if not action then
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

	action(player)
	return Enum.ProductPurchaseDecision.PurchaseGranted
end

Two rules make this safe. First, assign the property (ProcessReceipt = function ...) rather than connecting a signal — Roblox runs exactly one callback, and any others are silently ignored. Second, make the grant idempotent: Roblox retries the callback until you return PurchaseGranted, so record each processed receiptInfo.PurchaseId in a DataStore and grant at most once per ID. Granting coins inside the callback and then crashing before the return is exactly how players get refunded items for free.

Gamepasses do not go through ProcessReceipt. That callback is for Developer Products only; passes are covered by PromptGamePassPurchaseFinished plus the UserOwnsGamePassAsync check.

Common mistakes

Learn Luau by doing

Monetization code is small but unforgiving, and the failure modes only make sense once you have wired the prompt, the finished handler and the ownership cache yourself. On Luablox you can practise Luau interactively through /theory, sharpen the tricky parts with drills on /problems, and follow structured Luablox lessons that build from basics to full gameplay systems.

Frequently asked questions

How do I check if a player owns a gamepass?

Call MarketplaceService:UserOwnsGamePassAsync(player.UserId, passId) on the server, wrapped in pcall because it is a web request. Run it when the player joins and cache the result in a table so gameplay checks never hit the API repeatedly.

Gamepass or Developer Product — which one do I need?

A gamepass is a one-time, permanent unlock (VIP status, 2x speed, an extra island) detected with UserOwnsGamePassAsync. A Developer Product is consumable and repeatable (coins, potions, boosts) granted through ProcessReceipt. If the player should own it forever, use a gamepass.

Where do I handle ProcessReceipt?

In exactly one server Script, assigned as MarketplaceService.ProcessReceipt = function(receiptInfo) ... end. Look up the player with Players:GetPlayerByUserId, grant the product, remember the PurchaseId to prevent double-granting, and return Enum.ProductPurchaseDecision.PurchaseGranted (or NotProcessedYet to make Roblox retry).

Why doesn't PromptGamePassPurchaseFinished fire in my server script?

Usually the prompt was never opened: PromptGamePassPurchase must be called from a LocalScript with a valid pass ID that has a price set in the Creator Dashboard. A pass left Off Sale, or an ID copied from the wrong item page, opens no dialog at all.

Do gamepass purchases work while testing in Roblox Studio?

You can prompt purchases in Studio and the dialog opens, but test accounts complete them only in live games; in Studio, UserOwnsGamePassAsync returns false for test players. Verify ownership logic in a published test place or temporarily bypass the check behind a Studio-only debug flag.

Learn core Roblox scripting and Luau programming concepts, including: roblox gamepass script, roblox developer products, PromptGamePassPurchase, UserOwnsGamePassAsync, ProcessReceipt roblox.

Related pages