Payments
Accept Robux payments in-game using Developer Products. Staff create payment links in the dashboard; players pay in-game and the link is automatically marked paid.
How it works#
- Staff create a Payment Link in the dashboard (Items + Robux total).
- The link URL is shared with the customer.
- When the customer joins the game,
RoPermsPayments.luadetects their pending link and prompts them with the matching Developer Product. - The customer pays the Developer Product. Roblox calls your
ProcessReceipthandler. - The handler calls
RoPerms.CompletePayment(), which marks the link as PAID. - The payment link page shows a confirmation in real time.
ℹDrop-in handler
RoPermsPayments.lua is a self-contained Script that handles the entire payment flow. Place it in ServerScriptService and configure the PRODUCTS table — that is the only required customisation for most games.Step 1 — Create Developer Products#
RoPerms uses Roblox Developer Products (not Gamepasses) for payments because they can be purchased multiple times by the same player.
Create one Developer Product per Robux amount you expect to charge. For example, if you sell services for 500, 1000, and 2500 Robux, create three products. In the Roblox Creator Dashboard, note the numeric Product ID for each.
Step 2 — Configure RoPermsPayments.lua#
Open RoPermsPayments.lua and find the PRODUCTS table. Map each Robux amount to its Developer Product ID:
local PRODUCTS = {
-- [Robux amount] = Developer Product ID
[500] = 1234567890,
[1000] = 1234567891,
[2500] = 1234567892,
}Also set your API key and base URL near the top:
local CONFIG = {
apiKey = game:GetService("HttpService"):GetSecret("ROPERMS_KEY"),
baseUrl = "https://roperms.app",
-- How often (seconds) to poll for pending payment links
pollInterval = 10,
}Step 3 — Place the script#
- 1
Copy RoPermsPayments.lua
Place the file as a Script (not ModuleScript) insideServerScriptService. - 2
Do not use a separate ProcessReceipt
RoPermsPayments.luasetsMarketplaceService.ProcessReceipt. Only one script can set this handler in a place — do not set it elsewhere or the payments will not complete correctly. - 3
Publish and test
Publish your place. Create a test payment link in the dashboard for yourself, join the game, and verify you are prompted with the Developer Product. Check the dashboard — the link should flip to PAID after purchase.
SDK functions#
RoPerms.GetOpenPayments(player: Player): table
Returns a list of pending payment links for the player. Each entry has id, token, totalRobux, and items fields. Used internally by RoPermsPayments.lua but available if you want a custom payment UI.
local links = RoPerms.GetOpenPayments(player)
for _, link in ipairs(links) do
print(link.token, link.totalRobux)
endRoPerms.CompletePayment(player: Player, linkIdOrToken: string, receiptId: string): boolean
Marks a payment link as PAID. Pass the link's token or ID, and the Roblox receipt ID from ProcessReceipt. Returns true on success. Called automatically by RoPermsPayments.lua inside ProcessReceipt.
-- Inside a custom ProcessReceipt handler:
local function processReceipt(info)
local player = Players:GetPlayerByUserId(info.PlayerId)
if not player then return Enum.ProductPurchaseDecision.NotProcessedYet end
local ok = RoPerms.CompletePayment(player, pendingToken, tostring(info.PurchaseId))
if ok then
return Enum.ProductPurchaseDecision.PurchaseGranted
end
return Enum.ProductPurchaseDecision.NotProcessedYet
endConfiguring items in the dashboard#
In your org Settings, go to Payment Categories. Create categories (e.g., "Services", "Bundles") and add items with names and Robux prices.
Staff members with the payments.manage capability can then create payment links using items from this catalogue. The total Robux on the link must match one of the Developer Product amounts in your PRODUCTS table, or the in-game prompt will fail to find a matching product.
⚠Amount matching
Receipt deduplication#
RoPermsPayments.lua uses an OrderedDataStore to track processed receipt IDs. This prevents double-completing a payment if Roblox replays aProcessReceipt call (which can happen on server restart or when the callback times out).