Guide · 8 min read · Updated October 2026
Game pass and developer product scripts with AI, done right
Monetization is the part of a Roblox game that AI script generators get wrong most often, and the part exploiters look at first. This guide gives you the one prompt that wires game passes and developer products correctly, shows the Luau a good agent should produce, and explains the three rules that make it exploit-resistant. It works in any Roblox Studio AI tool that can edit your place; the prompt is written for ForgeBlox.
Passes vs products in one table
| Game pass | Developer product | |
|---|---|---|
| Bought | Once, owned forever | Many times, consumable |
| Examples | VIP, 2x Coins, Auto Collect, a skin | 500 Coins, Skip Stage, Revive |
| Check | UserOwnsGamePassAsync once on join (in a pcall), plus PromptGamePassPurchaseFinished to grant instantly | MarketplaceService.ProcessReceipt on the server, exactly one in the whole game |
| Prompt from | Client: PromptGamePassPurchase | Client: PromptProductPurchase |
| Typical price | 2x pass 199–399 R$, VIP 399–799 R$ | Coin packs 25–99 R$, skip stage 25–49 R$ |
The three rules
- Exactly one ProcessReceipt. It is a single callback. A second assignment anywhere in the game silently replaces the first, and purchases vanish. Keep it in
ServerScriptService/Monetizationand route every product through a table. - Grant idempotently, on the server, before returning. Roblox retries receipts. Record the purchase id in a DataStore, grant through your economy module so it is saved with the player data, and only then return
PurchaseGranted. The client never sends anything about a purchase. - One config module for ids. Put every pass and product id in
ReplicatedStorage/Config/Products. The AI writes placeholder ids; you create the real ones in the Creator Dashboard and paste them in.
The prompt
Add monetization to this place. Create ReplicatedStorage/Config/Products with
placeholder ids: game passes VIP (2x coins) and AutoCollect; developer products
Coins500, Coins2000, SkipStage. On join, check each pass with
UserOwnsGamePassAsync in a pcall and cache it in the player's data; also handle
PromptGamePassPurchaseFinished to grant immediately. Put the ONLY
MarketplaceService.ProcessReceipt in ServerScriptService/Monetization: look up
the product in a grants table, record receipt.PurchaseId in a PurchaseHistory
DataStore so a retry never double-grants, grant through the Economy module so it
is saved with the player, and return PurchaseGranted only after the save. Add a
Shop HUD with real prices from GetProductInfo (cached, pcall). Test one pass and
one product with Studio test purchases, screenshot the shop and the console.
What the agent should produce
The core of the server script looks like this. If your tool produced something that grants from a LocalScript, or that has no purchase-history check, send the prompt again with "fix ProcessReceipt to be idempotent and server-only."
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local Economy = require(game.ServerScriptService.Economy)
local Products = require(game.ReplicatedStorage.Config.Products)
local purchaseHistory = DataStoreService:GetDataStore("PurchaseHistory_v1")
local grants = {
[Products.Coins500] = function(player) Economy.AddCoins(player, 500, "product:Coins500") end,
[Products.Coins2000] = function(player) Economy.AddCoins(player, 2000, "product:Coins2000") end,
[Products.SkipStage] = function(player) Economy.SkipStage(player, 1) end,
}
MarketplaceService.ProcessReceipt = function(receipt)
local player = Players:GetPlayerByUserId(receipt.PlayerId)
if not player then return Enum.ProductPurchaseDecision.NotProcessedYet end -- retried when they rejoin
local key = receipt.PlayerId .. "_" .. receipt.PurchaseId
local ok, already = pcall(purchaseHistory.GetAsync, purchaseHistory, key)
if not ok then return Enum.ProductPurchaseDecision.NotProcessedYet end
if already then return Enum.ProductPurchaseDecision.PurchaseGranted end -- idempotent
local grant = grants[receipt.ProductId]
if not grant then return Enum.ProductPurchaseDecision.NotProcessedYet end
local granted = pcall(grant, player)
if not granted then return Enum.ProductPurchaseDecision.NotProcessedYet end
pcall(purchaseHistory.SetAsync, purchaseHistory, key, true)
return Enum.ProductPurchaseDecision.PurchaseGranted
end
Two details worth noticing: NotProcessedYet is returned whenever anything is uncertain, because Roblox will retry and that is the safe outcome; and the grant goes through Economy, the same module the rest of the game uses, so the reward is saved with the player data rather than in a separate place that can drift.
Testing before you publish
- Studio test purchases use test Robux and never charge anyone. Products only work for real after the game is published.
- Buy the same product twice in one test session and confirm the second purchase grants once and only once.
- Open the Output window during the test. A clean console is the goal; a warning from ProcessReceipt is a bug.
- Run two clients (Test ▸ Clients and Servers) and buy on one; the other must not receive anything.
Pricing guidance
Common ranges from published Roblox games in 2026: a 2x pass at 199–399 Robux, VIP at 399–799, small coin packs at 25–99, skip-stage at 25–49. Show the real price with GetProductInfo(id, Enum.InfoType.Product).PriceInRobux, in a pcall, cached, rather than hard-coding it in the UI.
See it in context: the tycoon guide adds a shop in message 4, the obby guide adds skip-stage, and the simulator guide adds a 2x pass. Costs are on the pricing page.
Related guides
- How to make a Roblox UI with AI: the shop panel and HUD on top of these scripts.
- Roblox AI script generators: what to use and how to check the Luau: a correct DataStore script and a 10-point review checklist.