In short
MarketplaceService.ProcessReceipt is the function Roblox calls after a player buys a developer product, so your game can give them what they paid for. Roblox only accepts it from the server, because the client is the player's own device and could grant itself anything. A LocalScript (or a ModuleScript a LocalScript required) that sets it gets this error. Keep the purchase prompt in the LocalScript and move the receipt handling to one Script in ServerScriptService.
- Client:
MarketplaceService:PromptProductPurchase(player, productId)when the button is pressed. - Server: one Script sets
MarketplaceService.ProcessReceipt, gives the item and returnsPurchaseGrantedonly once it is really given. - The same applies to the newer
MarketplaceService:BindReceiptHandler.
What the message means
Players.Builderman.PlayerGui.Shop.BuyCoins.LocalScript:12: Can only register ProcessReceipt callback on server
Other forms of the same message:
BindReceiptHandler can only be called from the server
Until a receipt is answered with Enum.ProductPurchaseDecision.PurchaseGranted, Roblox treats the purchase as not delivered and tries again later, for example when the player next joins. A receipt handler that never runs, because it was set in a LocalScript, means players pay and get nothing in that session.
The PromptProductPurchaseFinished event on the client only says the purchase window closed. Do not give items from it: the server receipt is the only reliable signal.
Causes and fixes
Two things cause this error in practice. Each one below has the smallest code that triggers it, then the fix.
- The shop button's LocalScript handles the receipt
- A shared module in ReplicatedStorage sets it, and a LocalScript requires it
The shop button's LocalScript handles the receipt
It feels natural to keep the prompt and the reward together in the button's script. The prompt part is allowed there; the receipt part is not.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local COINS_PRODUCT_ID = 0000000 -- your developer product ID
local button = script.Parent :: TextButton
button.Activated:Connect(function()
MarketplaceService:PromptProductPurchase(Players.LocalPlayer, COINS_PRODUCT_ID)
end)
-- Can only register ProcessReceipt callback on server
MarketplaceService.ProcessReceipt = function(_receiptInfo)
return Enum.ProductPurchaseDecision.PurchaseGranted
end
Delete the receipt part from the LocalScript, keep only the prompt, and handle receipts in a server Script like the one below.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local COINS_PRODUCT_ID = 0000000 -- your developer product ID
local function grantCoins(player: Player): boolean
local leaderstats = player:FindFirstChild("leaderstats")
local coins = leaderstats and leaderstats:FindFirstChild("Coins")
if coins and coins:IsA("IntValue") then
coins.Value += 100
return true
end
return false -- not ready yet: Roblox will try this receipt again later
end
MarketplaceService.ProcessReceipt = function(receiptInfo: { [string]: any }): Enum.ProductPurchaseDecision
local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
if player and receiptInfo.ProductId == COINS_PRODUCT_ID then
local ok, granted = pcall(grantCoins, player)
if ok and granted == true then
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
return Enum.ProductPurchaseDecision.NotProcessedYet
end
This is the smallest working handler. For a real game, record each PurchaseId in a data store together with the grant, so a receipt delivered twice is never granted twice: the developer products guide builds that version step by step.
A shared module in ReplicatedStorage sets it, and a LocalScript requires it
A Shop ModuleScript in ReplicatedStorage that both the client and the server require runs its top-level code on whichever side requires it. If it sets ProcessReceipt at the top, the client's copy fails.
local MarketplaceService = game:GetService("MarketplaceService")
local Shop = {}
Shop.COINS_PRODUCT_ID = 0000000
function Shop.prompt(player: Player)
MarketplaceService:PromptProductPurchase(player, Shop.COINS_PRODUCT_ID)
end
-- runs on the client too when a LocalScript requires this module:
-- Can only register ProcessReceipt callback on server
MarketplaceService.ProcessReceipt = function(_receiptInfo)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
return Shop
Keep shared modules free of receipt code. Shared data (product IDs, prices) can stay in ReplicatedStorage; the receipt handler lives in ServerScriptService and is the only code that sets ProcessReceipt.
local MarketplaceService = game:GetService("MarketplaceService")
local Shop = {}
Shop.COINS_PRODUCT_ID = 0000000
function Shop.prompt(player: Player)
MarketplaceService:PromptProductPurchase(player, Shop.COINS_PRODUCT_ID)
end
return Shop
Only one script should set ProcessReceipt. Setting it again from another server Script silently replaces the first handler, so products handled there stop being granted.
Fix it in your place
Paste the error, RoCode fixes it in your place
Copy the line from Output and paste it here. RoCode opens the script it names in the place you have open in Studio, works out which of the causes above it is, and edits the code there instead of handing you a snippet to paste.
RoCode is an AI agent for Roblox Studio: it reads the scripts in the place you have open, finds the line and edits it there, and every change is an undo point. It runs on Windows or macOS with a desktop browser. Free to start, with free credits every day.
How to find the line that causes it
- The path in the message names the script. A path through
PlayerGui,PlayerScriptsor a character means a LocalScript. - Search your place for
ProcessReceiptandBindReceiptHandlerwith Ctrl+Shift+F (Find All). There should be exactly one place that sets it, in ServerScriptService.
Questions
Where do I put ProcessReceipt in Roblox?
In one Script in ServerScriptService. LocalScripts only show the purchase prompt with PromptProductPurchase.
Can I give the item from PromptProductPurchaseFinished?
No. That event only says the window closed, and on the client a cheater can fake it. Grant developer products from the server's receipt handler.
What should ProcessReceipt return?
Enum.ProductPurchaseDecision.PurchaseGranted only after the item has really been given (and saved), and NotProcessedYet in every other case, so Roblox tries the receipt again later.
Can RoCode fix it?
Yes. Paste the Output line into RoCode. It reads your open place, moves the receipt handling into one server Script and keeps the prompt in your button.
Sources and further reading
- Roblox Creator Docs: MarketplaceService.ProcessReceipt. server only, and the decisions it returns
- Roblox Creator Docs: Developer products
- Roblox Creator Docs: MarketplaceService:PromptProductPurchase
How the code was checked: every block marked Fixed passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 5 November 2026), and every block marked Breaks compiles and fails only when it runs. The message wording was checked against the message strings in Roblox Studio itself (macOS build installed 2 November 2026) and the MarketplaceService reference on 5 November 2026. A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.