roblox-gamepasses — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited roblox-gamepasses (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Primary official source: https://create.roblox.com/docs/en-us/production/monetization/passes (plus developer-products.md for contrast, and the MarketplaceService class reference).
This skill focuses on getting game passes right the first time — correct ownership checking, server-only fulfillment, proper error handling, and modern personalization features that most older tutorials ignore.
See roblox-datastores for how to store "player owns this pass" state or associated perks, and roblox-networking for the Remote validation layer around purchase prompts.
MarketplaceService:PromptRobuxTransferAsync must be called from the server. Only Roblox Plus subscribers can initiate transfers, amounts are clamped to 10–500 Robux per transaction, the sender cannot equal the receiver, Roblox takes a 10% platform fee, and the recipient receives 90%. A BindReceiptHandler callback must process transfer receipts; donations are high-risk for abuse and should include anti-abuse checks (rate limits, alt detection, moderation, no quid-pro-quo rewards).For external sales on the game page Store tab: go to the pass → Sales → enable "Item for Sale" and set Robux price (1 to 1B).
Client side (LocalScript or UI module — only for prompting and optimistic display):
Server side (Script in ServerScriptService — the only place that grants benefits):
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local PASS_ID = 1234567890
local granted = {} -- idempotency guard: granted[player][passID]
local function grantPassBenefits(player: Player, passID: number)
if not player or not player:IsDescendantOf(Players) then
return
end
local playerGranted = granted[player]
if not playerGranted then
playerGranted = {}
granted[player] = playerGranted
end
if playerGranted[passID] then
return
end
playerGranted[passID] = true
-- Apply the actual privilege (attributes, table entry, DataStore flag, Remote to client for visuals, etc.)
-- This is the source of truth.
end
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player: Player, purchasedPassID: number, wasPurchased: boolean)
-- This event also fires on the client; never grant benefits from the client copy.
if not player or typeof(purchasedPassID) ~= "number" then
return
end
if wasPurchased and purchasedPassID == PASS_ID then
local ok, err = pcall(function()
grantPassBenefits(player, PASS_ID)
end)
if not ok then
warn("Failed to grant pass benefits:", err)
end
end
end)
Players.PlayerAdded:Connect(function(player: Player)
local success, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, PASS_ID)
end)
if success and owns then
local ok, err = pcall(function()
grantPassBenefits(player, PASS_ID)
end)
if not ok then
warn("Failed to apply owned pass benefits:", err)
end
elseif not success then
warn("UserOwnsGamePassAsync failed for", player.Name)
end
end)
Players.PlayerRemoving:Connect(function(player: Player)
granted[player] = nil
end)GetProductInfoAsync for dynamic UI (price, name, description, IsForSale): Use MarketplaceService:GetProductInfoAsync(id, Enum.InfoType.GamePass). Do this on the client for display, but never grant based on the result. The non-async counterpart GetProductInfo still exists, but GetProductInfoAsync is preferred.
All MarketplaceService purchase APIs (PromptGamePassPurchase, PromptPurchase, developer-product ProcessReceipt, PromptRobuxTransferAsync, etc.) require API Services to be enabled for the place (Home tab → Game Settings → Security → Enable Studio Access to API Services for Studio testing; live places use the deployed configuration). Game passes and developer products also require the experience to be published.
MarketplaceService:RankProductsAsync(arrayOfIdentifiers) — pass a table of up to 50 {InfoType = Enum.InfoType.GamePass, Id = ...}. Returns a personalized ranking for the current user as {ProductIdentifier, ProductInfo} items. Use sparingly; call once at join.MarketplaceService:RecommendTopProductsAsync({Enum.InfoType.GamePass, Enum.InfoType.Product}) — returns up to 50 recommended products the user is likely to engage with. Results usually exclude already-owned items, but verify in your UI. Use sparingly; call once at join.Surface these in "Recommended for you" or "Top picks" sections of your in-experience shop. This measurably improves conversion.
You can opt passes into the promotion pool so that users buying Robux packages may receive the pass for free (contextually relevant to their history).
Requirements (from the passes doc):
Opt-in via the pass's Promotions tab in the dashboard.
In Creator Dashboard → experience → Monetization → Passes → Analytics tab you get:
Use this data to decide pricing, which perks are compelling, and when to run promotions.
UserOwnsGamePassAsync and Prompt... calls.GetProductInfoAsync so regional pricing and optimizations work.PolicyService first: check PolicyService:GetPolicyInfoForPlayerAsync(player).ArePaidRandomItemsRestricted and IsPaidItemTradingAllowed before offering randomized paid content.MarketplaceService.ProcessReceipt can only be assigned once globally; assign it once in a single server script. The callback must return Enum.ProductPurchaseDecision.PurchaseGranted after successful fulfillment, or Enum.ProductPurchaseDecision.NotProcessedYet if fulfillment fails, because Roblox may redeliver the receipt until PurchaseGranted is returned.PromptRobuxTransferAsync are high-risk for abuse; implement rate limits, alt-account detection, moderation pipelines, and avoid granting in-experience advantages in exchange for transfers.Subscriptions offer users recurring benefits for a monthly fee, auto-renewing in Robux or local currency. Unlike passes (permanent), subscription benefits persist only while the user keeps paying. Up to 50 per experience; single-tiered (no mutually exclusive Bronze/Silver/Gold); regional pricing enabled by default for Robux-priced subs.
API surface (subscription IDs are strings like "EXP-11111111"):
MarketplaceService:GetUserSubscriptionStatusAsync(player, subscriptionId) — server-only, returns {IsSubscribed: boolean}.MarketplaceService:PromptSubscriptionPurchase(player, subscriptionId) — client prompt.MarketplaceService.PromptSubscriptionPurchaseFinished(player, subscriptionId, didTryPurchasing) — note didTryPurchasing is an attempt signal, not success; re-check status after a delay.Players.UserSubscriptionStatusChanged(player, subscriptionId) — server-only, fires on purchase/renewal/cancellation.MarketplaceService:GetSubscriptionProductInfoAsync(subscriptionId) and GetUserSubscriptionPaymentHistoryAsync(player, subscriptionId).Security (same posture as passes): prompt on client, check status and grant/revoke on server only, pcall everything, re-check on every join (subscriptions lapse), respect PolicyService:IsEligibleToPurchaseSubscription per player, persist nothing sensitive on the client.
Payouts: Robux-priced subs pay 70% each month. Local-currency subs pay 70% first month, 100% thereafter, with a 30-day hold. Robux subs are not refundable; local-currency subs are refundable within the hold window.
See references/subscriptions.md for the complete flow, client/server code, migration from passes, and gotchas.
As of July 24, 2025, Engagement-Based Payouts (formerly "Premium Payouts") and the Creator Affiliate program were discontinued and replaced by Creator Rewards. There is no longer a per-Premium-play-minute payout to integrate against.
Creator Rewards pays creators in two ways (no in-experience integration required — it's a platform-side program, but you should know it exists):
Official source: https://create.roblox.com/docs/en-us/creator-rewards
MarketplaceService:GetUserSubscriptionStatusAsync etc.).See the developer-products doc for the repeatable flow.
scripts/PassPurchaseHelper.lua — a client-side helper for game pass button state, price display, and prompting.This skill + roblox-datastores + roblox-networking gives you a complete, secure, modern game pass implementation that follows current rules and best practices.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.