Roblox is rolling out a feature that shows your developer products and passes outside your game: in your Game Details Page store, on the home page, in search, and on the page players see after they buy Robux. The player buys first and joins your game later with the item in hand. (Announcement, Sep 22 2026)
For developer products, Roblox lists two conditions (passes don’t need this):
- The product is marked Listed in Creator Hub → Monetization → Shop.
ProcessReceiptis implemented correctly.
If Roblox detects a Listed product that needs a fix, a banner appears in Monetization → Shop with a report. Products in that report aren’t shown outside your game. Inside your game, nothing changes. You may not see the banner yet, because the feature is still rolling out by account.
Roblox has not said how it decides that a
ProcessReceiptis “incorrect”. Points marked (docs) come from Roblox’s official documentation or announcement. Everything else is practical advice. Use this as a checklist, not a guarantee.
Why purchases made outside the game are different
When someone buys inside your game, their data is already loaded: leaderstats, profile, character.
When someone buys outside and then joins, your receipt callback runs when they join the server (docs). Your data system may not have loaded them yet.
If your handler fails at that point and returns NotProcessedYet, the docs say Roblox does not retry on a timer: on that server it only tries again if the player starts another developer product purchase, or when they join a server again (docs). A Roblox engineer said on Sep 21 that receipts are now polled every 10 seconds (bug report thread). Either way, a handler that gives up too early leaves a player who paid, joined, and got nothing, at least for a while.
Checklist: 8 common mistakes
1. No ProcessReceipt at all.
Receipts are auto-acknowledged, and “you can’t get a receipt back after it has been acknowledged” (docs).
2. Granting in PromptProductPurchaseFinished.
Don’t. The event firing “does not mean that a user has successfully purchased an item” (docs). A purchase made outside your game also never shows an in-game prompt, so only ProcessReceipt / BindReceiptHandler will see it.
3. Returning PurchaseGranted when nothing was given.
The example on the Developer Products docs page runs pcall(handler, receipt, player) and returns PurchaseGranted whenever the pcall succeeds. The same docs page says handlers “must return true”. If your handler quietly does nothing (for example, if gold then ... end while leaderstats aren’t loaded), the pcall still succeeds. The purchase is closed and the player gets nothing. Check that the handler actually returned true.
4. Not waiting for player data.
This is the outside-purchase problem described above. The fix is to wait briefly inside the callback until the player’s data is ready, then grant. Keep it under 10 seconds: the docs say a ProcessReceipt callback “can yield for as long as the server is running” (docs), but the same Roblox engineer explained that a handler still running when the next 10 s poll arrives causes “already being processed” errors (thread). If the data isn’t ready in time, return NotProcessedYet and let the next delivery try again.
5. Granting the same purchase twice.
The callback for one purchase “might run on two different servers at the same time”. It can also fail to be recorded even after you return PurchaseGranted, in which case it is delivered again (docs). Claim each PurchaseId atomically (for example with UpdateAsync, or in your session-locked profile) before you grant, and skip purchases you already granted.
6. A Listed product with no handler.
If no code in your game handles a Listed product, that purchase can’t be delivered correctly. Handle every developer product you list.
7. More than one script setting ProcessReceipt.
Roblox says to set the callback “one time in a single server-side Script” (docs). Kits and models that set their own can clash with yours. The newer MarketplaceService:BindReceiptHandler(Enum.ReceiptType.DeveloperProduct, handler, {productIds}) lets each system handle only its own product IDs. Anything that doesn’t match falls through to your existing ProcessReceipt (docs).
8. Listing items that shouldn’t be sold outside your game.
Don’t sell random items (loot boxes) or items limited by time, quantity, place, or user settings and roles outside your game (docs). Keep those Unlisted.
How to test
The official docs describe a test mode for external purchases. It is the closest official way to try a purchase made outside the game (docs):
- Creator Hub → Monetization → Developer Products → ⋮ → External Purchase Settings → Enable test mode.
- Open a cheap product and tick Allow external purchases. Test purchases cost real Robux (docs).
- Buy it from the Store tab of your game page, then join the game.
- Check that you received the item.
Then go back to the Shop report, select the products, and confirm they’re fixed.
Free module: ReceiptRouter
ReceiptRouter is a small open module that handles points 3, 4, 5 and 7 for you:
- Binding: uses
BindReceiptHandlerfiltered to your product IDs, so it never replaces another script’sProcessReceipt. IfBindReceiptHandlerdoesn’t exist, it falls back toProcessReceipt. If binding fails because of a conflict, it raises an error instead of silently overwriting anything. - Waiting: waits up to 8 s for your data to load (under the 10 s receipt poll), then returns
NotProcessedYetso the next delivery tries again. You define what “ready” means, and you can changeWaitSeconds. - Confirming: returns
Processedonly when your grant function returnedtrue.- If it returns
falseor errors, the router retries every second. - If it returns nothing (the classic bug), it warns and does not retry, so the item is never handed out over and over.
- If it returns
- No double grants: claims each
PurchaseIdin a DataStore withUpdateAsyncbefore granting, so a second server or a redelivery won’t grant it again. A claim abandoned by a crashed server is taken over after 2 minutes. - Clear warnings: the output says exactly what’s wrong (missing
return true, data never loaded, DataStore errors).
-- Script in ServerScriptService
local ReceiptRouter = require(game.ServerScriptService.ReceiptRouter)
local router = ReceiptRouter.new({
-- truthy when this player's data is loaded (leaderstats, a ProfileStore profile, etc.)
IsReady = function(player)
return player:FindFirstChild("leaderstats")
end,
})
router:Product(123456789, function(player, receipt) -- 500 Coins
player.leaderstats.Coins.Value += 500
return true -- only after giving it
end)
router:Product(987654321, function(player, receipt) -- 50 Gems (saved by your data system)
player:SetAttribute("Gems", (player:GetAttribute("Gems") or 0) + 50)
return true
end)
router:Start()
Using ProfileStore or ProfileService? Make IsReady return Profiles[player] and add to the profile’s data inside the grant function.
Rules for grant functions:
- Return
trueonce the item was given. - Return
false(or error) if it couldn’t be given. Nothing must have been given in that case. - Saving is your game’s job, like any other reward.
- In bind mode, register every Listed product. A product you don’t register falls through to your
ProcessReceipt. If you don’t have one, Roblox auto-acknowledges it.
Get it: ReceiptRouter on the Creator Store. It’s free and open: read every line before you use it.
Tested
The module was tested offline in 65 checks covering:
- the player not being in the server, data loading late or never, and the player leaving while it waits;
- a grant without
return true, a grant returningfalseand thentrue, and errors followed by retries; - a redelivered receipt, two servers handling the same purchase at once, and an abandoned claim;
- temporary and permanent DataStore errors, and Studio without API access;
- a binding conflict, and the
ProcessReceiptfallback; - the default wait staying under 10 seconds.
Please still run test mode in your own game before confirming the fix.
Sources
- Roblox announcement, Sep 22 2026: Players Can Now Find In-Game Items Outside Your Game. Players Can Now Find In-Game Items Outside Your Game
- Roblox docs:
MarketplaceService.ProcessReceipt,MarketplaceService:BindReceiptHandler. MarketplaceService | Documentation - Roblox Creator Hub - Roblox staff on receipt polling (10 s) and “already being processed”: Developer Product Receipts Are Permanently Stuck, Sep 21 2026. Developer Product Receipts Are Permanently Stuck
- Roblox docs: Developer products → Outside your game / Test mode / Handle a developer product purchase. Developer Products | Documentation - Roblox Creator Hub
I also make Flash Offers Kit, which uses this same receipt logic since v1.1. Happy to answer any questions below.
Edit, Oct 3: ReceiptRouter v1.0.1. Default wait is now 8 s instead of 60 s, after Roblox staff mentioned the 10 s receipt poll. Points 4 and “Why purchases made outside the game are different” updated.