| name | monetization |
| description | Implements monetization systems including GamePasses, Developer Products, Premium benefits, and ethical monetization patterns. Use when adding in-game purchases, premium features, or any Robux-based transactions. |
| allowed-tools | Read, Write, Edit, Glob, Grep |
Roblox Monetization Systems
When implementing monetization, follow these patterns for secure and player-friendly purchases.
MarketplaceService Basics
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local GAME_PASSES = {
VIP = 123456789,
DoubleCash = 234567890,
SpeedBoost = 345678901
}
local DEV_PRODUCTS = {
Cash_100 = 111111111,
Cash_500 = 222222222,
Cash_1000 = 333333333,
Revive = 444444444
}
Game Passes
Checking Ownership
local function ownsGamePass(player, passId)
local success, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, passId)
end)
if success then
return owns
else
warn("Failed to check game pass ownership:", owns)
return false
end
end
local gamePassCache = {}
local function getGamePassOwnership(player, passId)
local key = player.UserId .. "_" .. passId
if gamePassCache[key] ~= nil then
return gamePassCache[key]
end
local owns = ownsGamePass(player, passId)
gamePassCache[key] = owns
return owns
end
Players.PlayerRemoving:Connect(function(player)
for key in pairs(gamePassCache) do
if key:find(tostring(player.UserId)) then
gamePassCache[key] = nil
end
end
end)
Prompting Purchase
local function promptGamePass(player, passId)
local success, err = pcall(function()
MarketplaceService:PromptGamePassPurchase(player, passId)
end)
if not success then
warn("Failed to prompt game pass:", err)
end
end
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, wasPurchased)
if wasPurchased then
local key = player.UserId .. "_" .. passId
gamePassCache[key] = true
applyGamePassBenefits(player, passId)
print(player.Name, "purchased game pass:", passId)
end
end)
Applying Benefits
local function applyGamePassBenefits(player, passId)
if passId == GAME_PASSES.VIP then
player:SetAttribute("VIP", true)
local tags = player:GetAttribute("ChatTags") or ""
player:SetAttribute("ChatTags", "[VIP] " .. tags)
player:SetAttribute("DailyBonusMultiplier", 2)
elseif passId == GAME_PASSES.DoubleCash then
player:SetAttribute("CashMultiplier", 2)
elseif passId == GAME_PASSES.SpeedBoost then
player:SetAttribute("SpeedBoost", 1.5)
local character = player.Character
if character then
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid.WalkSpeed = 16 * 1.5
end
end
end
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(
name, passId (GAME_PASSES)
getGamePassOwnership(player, passId)
applyGamePassBenefits(player, passId)
)
)
Developer Products (Consumables)
Processing Receipts (CRITICAL)
local purchaseHistory = {}
MarketplaceService.ProcessReceipt = function(receiptInfo)
local purchaseKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
if purchaseHistory[purchaseKey] then
return Enum.ProductPurchaseDecision.PurchaseGranted
end
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
if not player then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local productId = receiptInfo.ProductId
local success = false
if productId == DEV_PRODUCTS.Cash_100 then
success = grantCash(player, 100)
elseif productId == DEV_PRODUCTS.Cash_500 then
success = grantCash(player, 500)
elseif productId == DEV_PRODUCTS.Cash_1000 then
success = grantCash(player, 1000)
elseif productId == DEV_PRODUCTS.Revive then
success = revivePlayer(player)
else
warn("Unknown product:", productId)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
if success then
purchaseHistory[purchaseKey] =
savePurchaseRecord(player, receiptInfo)
Enum.ProductPurchaseDecision.PurchaseGranted
Enum.ProductPurchaseDecision.NotProcessedYet
currentCash = DataManager.get(player, )
DataManager.set(player, , currentCash + amount)
multiplier = player:GetAttribute()
multiplier >
bonus = amount * (multiplier - )
DataManager.set(player, , currentCash + amount + bonus)
CashNotificationRemote:FireClient(player, amount, bonus)
CashNotificationRemote:FireClient(player, amount, )
DataManager.save(player)
character = player.Character
character
humanoid = character:FindFirstChildOfClass()
humanoid
humanoid.Health >
humanoid.Health = humanoid.MaxHealth
player:SetAttribute(, )
ReviveEffectRemote:FireAllClients(character)
Prompting Products
local function promptProduct(player, productId)
local success, err = pcall(function()
MarketplaceService:PromptProductPurchase(player, productId)
end)
if not success then
warn("Failed to prompt product:", err)
end
end
local function onPlayerDied(player)
task.delay(2, function()
RevivePromptRemote:FireClient(player, DEV_PRODUCTS.Revive)
end)
end
Product Info Display
local function getProductInfo(productId)
local success, info = pcall(function()
return MarketplaceService:GetProductInfo(productId, Enum.InfoType.Product)
end)
if success then
return {
name = info.Name,
description = info.Description,
price = info.PriceInRobux,
icon = "rbxassetid://" .. info.IconImageAssetId
}
end
return nil
end
local productInfoCache = {}
local function getCachedProductInfo(productId)
if not productInfoCache[productId] then
productInfoCache[productId] = getProductInfo(productId)
end
return productInfoCache[productId]
end
Premium Benefits
local function isPremium(player)
return player.MembershipType == Enum.MembershipType.Premium
end
local function applyPremiumBenefits(player)
if isPremium(player) then
player:SetAttribute("IsPremium", true)
player:SetAttribute("CashMultiplier",
(player:GetAttribute("CashMultiplier") or 1) * 1.5)
player:SetAttribute("XPMultiplier",
(player:GetAttribute("XPMultiplier") or 1) * 1.5)
player:SetAttribute("DailyBonusMultiplier",
(player:GetAttribute("DailyBonusMultiplier") or 1) * 2)
player:SetAttribute("PremiumItemsUnlocked", true)
end
end
Players.PlayerMembershipChanged:Connect(function(player)
if isPremium(player) then
applyPremiumBenefits(player)
PremiumWelcomeRemote:FireClient(player)
end
end)
success, err = (
MarketplaceService:PromptPremiumPurchase(player)
)
success
warn(, err)
Shop UI Patterns
Shop Item Template
local ShopItems = {
cash = {
{id = DEV_PRODUCTS.Cash_100, amount = 100, icon = "rbxassetid://123"},
{id = DEV_PRODUCTS.Cash_500, amount = 500, icon = "rbxassetid://124", bonus = 50},
{id = DEV_PRODUCTS.Cash_1000, amount = 1000, icon = "rbxassetid://125", bonus = 200}
},
gamePasses = {
{id = GAME_PASSES.VIP, name = "VIP", description = "VIP tag + 2x daily bonus"},
{id = GAME_PASSES.DoubleCash, name = "2x Cash", description = "Double all cash earnings"},
{id = GAME_PASSES.SpeedBoost, name = "Speed Boost", description = "50% faster movement"}
}
}
GetShopItemsRemote.OnClientEvent:Connect(function(items)
for _, item in ipairs(items.cash) do
local info = MarketplaceService:GetProductInfo(item.id, Enum.InfoType.Product)
local button = createShopButton()
button.Icon.Image = item.icon
button.Amount.Text = item.amount .. (item.bonus and " +" .. item.bonus or "")
button.Price.Text = info.PriceInRobux .. " R$"
button.MouseButton1Click:Connect(function()
MarketplaceService:PromptProductPurchase(Players.LocalPlayer, item.id)
end)
end
end)
Purchase Confirmation UI
local function confirmPurchase(productName, robuxCost)
local result = showConfirmDialog(
"Confirm Purchase",
"Buy " .. productName .. " for " .. robuxCost .. " Robux?",
{"Yes", "No"}
)
return result == "Yes"
end
PurchaseButton.MouseButton1Click:Connect(function()
local info = getCachedProductInfo(selectedProductId)
if info.price >= 100 then
if not confirmPurchase(info.name, info.price) then
return
end
end
MarketplaceService:PromptProductPurchase(LocalPlayer, selectedProductId)
end)
Purchase Persistence
Save Purchase Records
local PurchaseStore = DataStoreService:GetDataStore("Purchases_v1")
local function savePurchaseRecord(player, receiptInfo)
local key = "Player_" .. player.UserId
pcall(function()
PurchaseStore:UpdateAsync(key, function(data)
data = data or {purchases = {}}
table.insert(data.purchases, {
productId = receiptInfo.ProductId,
purchaseId = receiptInfo.PurchaseId,
time = os.time(),
robuxSpent = receiptInfo.CurrencySpent
})
while #data.purchases > 100 do
table.remove(data.purchases, 1)
end
return data
end)
end)
end
local function getPurchaseHistory(player)
local key = "Player_" .. player.UserId
local success, data = pcall(function()
return PurchaseStore:GetAsync(key)
end)
success data
data.purchases {}
{}
Grant Missed Purchases
local function checkPendingPurchases(player)
local history = getPurchaseHistory(player)
local grantedIds = DataManager.get(player, "grantedPurchases") or {}
for _, purchase in ipairs(history) do
if not grantedIds[purchase.purchaseId] then
local granted = grantProduct(player, purchase.productId)
if granted then
grantedIds[purchase.purchaseId] = true
end
end
end
DataManager.set(player, "grantedPurchases", grantedIds)
end
Ethical Monetization Guidelines
DO:
- Clearly display prices before purchase
- Allow players to earn most things through gameplay
- Make premium items cosmetic or time-saving, not power-increasing
- Provide value at every price point
- Respect player's time and money
DON'T:
- Create artificial scarcity or FOMO
- Hide true costs behind multiple currencies
- Require purchases to progress or compete
- Target children with manipulative dark patterns
- Make gameplay frustrating to encourage purchases
Fair Pricing Examples
local SHOP_CONFIG = {
cashPacks = {
{robux = 25, cash = 100, bonus = 0},
{robux = 50, cash = 250, bonus = 50},
{robux = 100, cash = 600, bonus = 100},
{robux = 200, cash = 1500, bonus = 300}
},
gamePasses = {
vip = {robux = 199, benefits = "Permanent VIP status + 2x daily bonus"},
doubleCash = {robux = 149, benefits = "Permanent 2x cash multiplier"}
}
}
Spending Limits (Self-Regulation)
local function trackSpending(player, robuxSpent)
local today = os.date("%Y-%m-%d")
local spendingKey = "Spending_" .. today
local todaySpent = player:GetAttribute(spendingKey) or 0
todaySpent = todaySpent + robuxSpent
player:SetAttribute(spendingKey, todaySpent)
if todaySpent >= 1000 then
SpendingWarningRemote:FireClient(player,
"You've spent " .. todaySpent .. " Robux today. Consider taking a break!")
end
end
MarketplaceService.ProcessReceipt = function(receiptInfo)
if success then
trackSpending(player, receiptInfo.CurrencySpent)
end
end
Complete Shop Implementation
local MonetizationService = {}
MonetizationService.GAME_PASSES = {
VIP = {id = 123456789, benefits = {"VIPTag", "DoubleDailyBonus"}},
DoubleCash = {id = 234567890, benefits = {"CashMultiplier"}},
PetSlots = {id = 345678901, benefits = {"ExtraPetSlots"}}
}
MonetizationService.DEV_PRODUCTS = {
Cash_100 = {id = 111111111, grant = {"cash", 100}},
Cash_500 = {id = 222222222, grant = {"cash", 500}},
Gems_10 = {id = 333333333, grant = {"gems", 10}},
Revive = {id = 444444444, action = "revive"}
}
local ownershipCache = {}
function MonetizationService.init()
MarketplaceService.ProcessReceipt = function(receiptInfo)
return MonetizationService.processReceipt(receiptInfo)
end
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, purchased)
if purchased then
MonetizationService.onGamePassPurchased(player, passId)
end
end)
Players.PlayerAdded:Connect(function
player.CharacterAdded:Connect(
MonetizationService.applyAllBenefits(player)
)
)
passConfig = MonetizationService.GAME_PASSES[passName]
passConfig
cacheKey = player.UserId .. .. passConfig.id
ownershipCache[cacheKey] ~=
ownershipCache[cacheKey]
success, owns = (
MarketplaceService:UserOwnsGamePassAsync(player.UserId, passConfig.id)
)
success
ownershipCache[cacheKey] = owns
owns
passName, (MonetizationService.GAME_PASSES)
MonetizationService.ownsGamePass(player, passName)
_, benefit (.benefits)
MonetizationService.applyBenefit(player, benefit)
player.MembershipType == Enum.MembershipType.Premium
MonetizationService.applyBenefit(player, )
benefit ==
player:SetAttribute(, )
benefit ==
player:SetAttribute(, )
benefit ==
player:SetAttribute(, )
benefit ==
player:SetAttribute(, )
benefit ==
player:SetAttribute(, )
player:SetAttribute(, )
player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
player
Enum.ProductPurchaseDecision.NotProcessedYet
productId = receiptInfo.ProductId
granted =
productName, (MonetizationService.DEV_PRODUCTS)
.id == productId
.grant
currency, amount = .grant[], .grant[]
granted = MonetizationService.grantCurrency(player, currency, amount)
.action ==
granted = MonetizationService.revivePlayer(player)
granted
DataManager.save(player)
Enum.ProductPurchaseDecision.PurchaseGranted
Enum.ProductPurchaseDecision.NotProcessedYet
current = DataManager.get(player, currency)
multiplier = player:GetAttribute(currency:(,):() .. currency:() .. )
finalAmount = .(amount * multiplier)
DataManager.set(player, currency, current + finalAmount)
CurrencyGrantedRemote:FireClient(player, currency, amount, finalAmount - amount)
character = player.Character
character
humanoid = character:FindFirstChildOfClass()
humanoid humanoid.Health >
humanoid.Health = humanoid.MaxHealth
MonetizationService