Loading...
Loading...
Robux monetization - developer products with idempotent ProcessReceipt, passes, subscriptions, rewarded video ads, personalized shops, regional pricing and trade arbitrage, paid random item policy (PolicyService), commerce products, private servers, Roblox Plus. Use when adding purchases, shops, game passes, subscriptions, ads, or loot boxes, or debugging lost/duplicated purchases.
npx skill4agent add el4cteo/roblox-skills roblox-monetization| Type | Buy | Use for | Grant via |
|---|---|---|---|
| Developer product | Repeatedly | Currency, consumables, boosts, revives | |
| Pass | Once, permanent | VIP, permanent perks, extra slots | |
| Subscription | Monthly, revocable | Recurring perks (daily currency, VIP) | |
| Rewarded video ad | Free (watch ad) | Small rewards (≈3–10 Robux value) | Developer product granted through |
| Commerce product | Real money (physical goods) | Merch with bundled digital benefits | |
| Private server | Monthly | Paid private servers | Configured in Creator Dashboard |
PromptProductPurchaseFinishedMarketplaceService.ProcessReceiptPurchaseGrantedNotProcessedYetPurchaseIdPurchaseIdNotProcessedYet--!strict
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
type PlayerData = { coins: number, purchaseIds: { string } }
type ReceiptInfo = {
PlayerId: number,
ProductId: number,
PurchaseId: string,
CurrencySpent: number,
CurrencyType: Enum.CurrencyType,
PlaceIdWherePurchased: number,
}
local MAX_REMEMBERED_PURCHASES = 100
-- Provided by your data layer (ProfileStore profile.Data, etc.).
local function getLoadedData(_player: Player): PlayerData?
return nil
end
local function saveNow(_player: Player): boolean
return true
end
local PRODUCTS: { [number]: (Player, PlayerData) -> () } = {
[1234567] = function(_player, data) -- 100 coins
data.coins += 100
end,
}
local function processReceipt(receipt: ReceiptInfo): Enum.ProductPurchaseDecision
local player = Players:GetPlayerByUserId(receipt.PlayerId)
if not player then
return Enum.ProductPurchaseDecision.NotProcessedYet -- granted on their next visit
end
local data = getLoadedData(player)
local deadline = os.clock() + 20
while not data and player.Parent == Players and os.clock() < deadline do
task.wait(0.5)
data = getLoadedData(player)
end
if not data then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
if table.find(data.purchaseIds, receipt.PurchaseId) then
return Enum.ProductPurchaseDecision.PurchaseGranted -- already granted earlier
end
local handler = PRODUCTS[receipt.ProductId]
if not handler then
warn(`No handler for product {receipt.ProductId}`)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local ok, err = pcall(handler, player, data)
if not ok then
warn(`Grant failed for {receipt.PurchaseId}: {err}`)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
table.insert(data.purchaseIds, receipt.PurchaseId)
while #data.purchaseIds > MAX_REMEMBERED_PURCHASES do
table.remove(data.purchaseIds, 1)
end
if not saveNow(player) then
return Enum.ProductPurchaseDecision.NotProcessedYet -- the idempotency list prevents a double grant
end
return Enum.ProductPurchaseDecision.PurchaseGranted
end
MarketplaceService.ProcessReceipt = processReceipt :: anyPurchaseIdprofile.LastSavedDataprofile:Save()MarketplaceService:PromptProductPurchase(Players.LocalPlayer, productId)--!strict
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local VIP_PASS_ID = 987654
local function grantVip(player: Player)
player:SetAttribute("VIP", true) -- apply perks from one place
end
local function checkPass(player: Player)
local ok, owns = pcall(MarketplaceService.UserOwnsGamePassAsync, MarketplaceService, player.UserId, VIP_PASS_ID)
if ok and owns then
grantVip(player)
end
end
Players.PlayerAdded:Connect(checkPass)
for _, player in Players:GetPlayers() do
task.spawn(checkPass, player)
end
-- Fires on the server too; re-verify ownership rather than trusting `purchased` alone.
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, purchased)
if purchased and passId == VIP_PASS_ID then
checkPass(player)
end
end)UserOwnsGamePassAsyncMarketplaceService:GetUserSubscriptionStatusAsync(player, "EXP-..."){ IsSubscribed, IsRenewing }Players.UserSubscriptionStatusChanged(player, subscriptionId)IsSubscribedMarketplaceService:PromptSubscriptionPurchase(player, subscriptionId)MarketplaceService:RankProductsAsync(identifiers)RecommendTopProductsAsync({ Enum.InfoType.Product, Enum.InfoType.GamePass })task.spawnGetProductInfoAsync(id, Enum.InfoType.Product)GetDeveloperProductsAsync()receipt.CurrencySpentPolicyService:GetPolicyInfoForPlayerAsync(player)ArePaidRandomItemsRestrictedIsPaidItemTradingAllowedMarketplaceService:GetUsersPriceLevelsAsync(userIds)PolicyServiceroblox-data-storesroblox-securityroblox-uiroblox-analytics-liveops