I don't understand how i should manage Process Receipt

If I need to have only one ProcessReceipt script, how can I handle multiple products in it? For example, I have a dev product that gives +100 currency and another that gives x2 speed for 1 minute. Do all of them need to be in the same script? And what’s the best way to organize it if the code gets really long? Thanks for your help.

The ProcessReceipt callback should only be set once (therefore can only be set in a single location, typically a Script). A callback is different than an event connection because a callback can only have a single “handler” whereas an event may have multiple “listeners” that each do something different with the data passed during each call. Additionally, typically a callback will return some information about what action was taken; In the case of the ProcessReceipt callback, an Enum.ProductPurchaseDecision must be returned. (see docs for more info)

To handle multiple different products, you simply need to implement some logic in the callback handler that differentiates between the product being purchased (considering other factors such as which client is making the purchase too, of course). A simple implementation might look like this:

--In a Script located in ServerScriptService
local MarketplaceService = game:GetService("MarketplaceService")

MarketplaceService.ProcessReceipt = function(receiptInfo)
   
   local productId = receiptInfo.ProductId --Using this value, we can differentiate between products
   --Replace the values below with developer product ids from your experience
   if productId == 123456 then
      --Provide effects of this product
   elseif productId == 234567 then
      --Provide effects of this product
   elseif productId == 345678 then
      --You can keep adding cases for all of your developer products
   else
      warn(`Unknown ProductId: {productId}`) --Ideally, this case should never happen
      return Enum.ProductPurchaseDecision.NotProcessedYet
   end

end

A good way to organize it too is to make the actual handler a module script. Then you can register functions in that get added to a table using the id as a key. Then the purchase can just do registeredCallbacks[id](…) assuming you check that registeredCallbacks[id] exists.

So then you can write a function and just do handler.RegisterProduct(id, func)

So for example the actual module script would be like this

local registeredProducts = {}


local productHandler = {}
function productHandler.RegisterProduct(id, func)
    assert(registeredProducts[id] == nil, "Attempting to create a duplicate handler for product id: " .. id)
    --maybe also check that the id is a valid type for the id and check that the func is actually a function
    registeredProducts[id] = func
end


local function processReceipt(info)
    if registeredProducts[id] then
        registeredProducts[id](info)
    end
    --dont forget to handle the case it wasn't found, make sure to do the returns and likely put this whole thing in a pcall for safety.  Remember if this breaks, you want it broken in favor of players.
end
return productHandler

Thats mostly how I set it up in my stuff though with some minor changes and of course adding in the checks I only comment about here.

1 Like

Is that means i need to have remote events for developer products? For an example if my code is too long.

I am curious if i can get the info from another script to script. Like can i inform other scripts about the process is done and the payment is ok.

You may only need a RemoteEvent to tell the client that they now own the product. iirc the setup is as follows:

  1. CLIENT - Player prompts product purchase, popup appears, they complete payment
  2. SERVER - MarketplaceService.ProcessReceipt event is triggered, do server logic here to give player their assets/status
  3. SERVER - Fire a remote event back to the player
  4. CLIENT - Display message “You now own {thing}!”

You can either use ModuleScripts, which run once per environment (Client, Server), and can be changed, read, store functions, which multiple scripts can access. Example, you make in the ModuleScript a variable in the array called “IsRaining”. Any Script can now access that variable.
Note: ModuleScripts only run once on the Client and on the Server, which means if you change something on the Server, the Client won’t be able to see that change.

Alternatively, you can use BindableEvents, which is basically a RemoteEvent that can send information to another Script of the same environment.

BindableEvents:

Server → Server
Client → Client