# MarketplaceService

MarketplaceService : `Instance`

## Overview

MarketplaceService is a service responsible for handling in-world product purchases and processing delivery upon successful payment.

Creators must define the ProcessReceipt callback function to ensure that deliveries are not missed when a product is sold, and are responsible for using DataStore or similar mechanisms to make sure deliveries complete successfully.

## Properties

### ProcessReceipt

`Value`

A callback function that processes undelivered receipts from successfully purchased products.

#### Code Samples

## Methods

### GetProductInfo

Returns the product information corresponding to the product ID (productId) and product type (Enum.InfoType).

#### Parameters

| `number` ProductId       | The ID of the product.   |
| ------------------------ | ------------------------ |
| `Enum.InfoType` InfoType | The type of the product. |

#### Return

| `Value` | <p>A dictionary containing the product information.</p><ul><li><code>string</code> Name: Product name</li><li><code>string</code> Description: Product description</li><li><code>number</code> ProductId: Product ID</li><li><code>string</code> ProductType: Product type</li><li><code>number</code> PriceInBLUC: Product price</li><li><code>number</code> Created: Time the product was created (UNIX timestamp)</li><li><code>number</code> Updated: Time the product was last updated (UNIX timestamp)</li></ul> |
| ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

#### Code Samples

```lua
local MarketplaceService = game:GetService("MarketplaceService")

local function Request_GetProductInfo(productId)
    local success, errorOrProductInfo = pcall(function()
        return MarketplaceService:GetProductInfo(productId, Enum.InfoType.Product)
    end)

    if not success then
        print("Error: " .. errorOrProductInfo .. " / ProductId : " .. productId)

    else
        local productInfo = errorOrProductInfo
        print("World Product Name: " .. tostring(productInfo.Name))
        print("ProductId: " .. tostring(productInfo.ProductId))
        print("ProductType: " .. tostring(productInfo.ProductType))
        print("PriceInBLUC: " .. tostring(productInfo.PriceInBLUC))
        print("Description: " .. tostring(productInfo.Description))
        print("Created: " .. productInfo.Created)
        print("Updated: " .. productInfo.Updated)
    end
end
```

### GetWorldProductsAsync

Returns a Pages object containing information about all world products.

#### Parameters

#### Return

| `Pages` | An object containing information about every world product in the current world. |
| ------- | -------------------------------------------------------------------------------- |

#### Code Samples

```lua
local MarketplaceService = game:GetService("MarketplaceService")

local function Request_GetWorldProductsAsync()
    local success, errorOrWorldProducts = pcall(function()
        return MarketplaceService:GetWorldProductsAsync()
    end)

    if not success then
        print("Error: " .. errorOrWorldProducts)

    else
        local worldProducts = errorOrWorldProducts

        local pageCount = 1
        local dataList = {}

        while true do
            local currentPage = worldProducts:GetCurrentPage()

            -- Exit loop if it's the last page
            if worldProducts.IsFinished or currentPage == nil then
                print(pageCount .. " page IsFinished : " .. tostring(worldProducts.IsFinished))
                break
            else
                worldProducts:AdvanceToNextPageAsync()
                pageCount = pageCount + 1
            end

            -- Each page contains up to 100 product entries
            for _, productInfo in pairs(currentPage) do
                local i = #dataList + 1

                print("------ " .. i .. " ------")
                print("World Product Name: " .. tostring(productInfo.Name))
                print("ProductId: " .. tostring(productInfo.ProductId))

                print("ProductType: " .. tostring(productInfo.ProductType))
                print("PriceInBLUC: " .. tostring(productInfo.PriceInBLUC))
                print("Description: " .. tostring(productInfo.Description))
                print("Created: " .. productInfo.Created)
                print("Updated: " .. productInfo.Updated)

                table.insert(dataList, productInfo)
            end
        end
    end
end
```

### PromptProductPurchase

Requests the purchase of the product corresponding to the world product ID (productId). (A purchase window is displayed through the system UI.)

#### Parameters

| `Player` Player    | The Player who will purchase the product. |
| ------------------ | ----------------------------------------- |
| `number` ProductId | The world product ID.                     |

#### Return

| `void` |   |
| ------ | - |

#### Code Samples

```lua
local MarketplaceService = game:GetService("MarketplaceService")

local function Request_PromptProductPurchase(player, productId)
    local success, error = pcall(function()
        MarketplaceService:PromptProductPurchase(player, productId)
    end)

    if not success then
        print("Error: " .. error .. " / ProductId : " .. productId)
    end
end
```

## Events

### PromptProductPurchaseFinished

This event is triggered when the purchase window opened by a purchase request (PromptProductPurchase) is closed. If the purchase succeeds, `true` is passed to isPurchased; if the user cancels or the purchase fails, `false` is passed.

This event should only be used to detect whether the purchase window has been closed. <mark style="color:red;">**It must never be used to process delivery for purchased products.**</mark>

#### Parameters

| `string` UserId     | The UserId of the Player who requested the purchase. |
| ------------------- | ---------------------------------------------------- |
| `number` ProductId  | The ID of the requested product.                     |
| `bool` bIsPurchased | Whether the purchase succeeded.                      |

#### Code Samples

```lua
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")

local function OnPromptPurchaseFinished(userId, productId, isPurchased)
    local player = Players:GetPlayerByUserId(userId)

    print(player.Name .. " / ProductID : " .. productId .. " / isPurchased : " .. tostring(isPurchased))
end
MarketplaceService.PromptProductPurchaseFinished:Connect(OnPromptPurchaseFinished)
```

## Callback

### ProcessReceipt

An event is triggered that returns information about **undelivered receipts** among successfully purchased products.

**Trigger Conditions**

* When a world product is successfully purchased (the successful purchase popup is shown to the user),
  * if there are any undelivered products, **those previous pending items will also be triggered together** when the new purchase is made.
* When the user **connects (or reconnects)** to the server

**How to Update Delivery Status**

* After successfully delivering the product, return Enum.ProductPurchaseDecision.**PurchaseGranted.**

**Important Notes**

* The ProcessReceipt event should be connected **only once in a server-side script**.
* This callback **can yield** indefinitely and remains valid until it receives a response, as long as the server is running.
* If there are multiple undelivered receipts, **each one will be triggered individually**, and the order of these callbacks is non-deterministic.
* The callback will only be triggered when the user is **present on the server**.
  * However, the result of the callback may still be recorded on the backend even if the user is no longer on the server.
* Returning **PurchaseGranted** from the callback does not guarantee that the backend will successfully record it. In such cases, the receipt status remains unchanged (remains undelivered).
* Products in an **undelivered state** will have their funds held in an **Escrow** state.

#### Parameters

| `table` Receipt | <p>A dictionary containing the receipt information for a successfully purchased product.</p><ul><li><code>string</code> PurchaseId: Receipt ID</li><li><code>string</code> PlayerId: The Player's UserId</li><li><code>number</code> ProductId: Product ID</li><li><code>number</code> CurrencySpent: Amount of currency spent on the transaction</li><li><code>number</code> PurchaseDateTime: Time the product was purchased (UNIX timestamp)</li></ul> |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

#### Return

| `Enum.ProductPurchaseDecision` | <p>The delivery status of the world product.</p><ul><li>PurchaseGranted: The product has been successfully delivered to the Player.</li><li>NotProcessedYet: The product has not yet been delivered.</li></ul> |
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |

#### Code Samples

```lua
local Players = game:GetService("Players")
local MarketplaceService = game:GetService("MarketplaceService")

local ProductDeliverer = {}

-----------------------------------------------------------------------------
-- You can define a function for each product ID in a table as shown below to 
-- implement custom delivery logic for each product.
ProductDeliverer[Enter the product number here.] = function(player)
    local success, resultOrError = pcall(function()
        -- Deliver the product to the player and handle saving using DataStore
        
        -- Tip.
        -- When saving product information using DataStore
        -- it's recommended to use IncrementAsync or UpdateAsync
        -- to prevent network conflicts or race conditions.
                
        -- Return "true" when the product has been successfully delivered and saved
        return true
    end)
    
    if success and resultOrError then
        return true
        
    else
        return false, resultOrError
    end
end

-----------------------------------------------------------------------------
-- Callback for handling the receipt triggered after a successful product purchase
local function OnProcessReceipt(receiptInfo)	
    -- Receipt information
    local success, error = pcall(function()	
        print("PurchaseId: " .. receiptInfo.PurchaseId)
        print("UserId: " .. receiptInfo.PlayerId)
        print("ProductId: " .. receiptInfo.ProductId)
        print("CurrencySpent: " .. receiptInfo.CurrencySpent)
        print("PurchaseDateTime: " .. receiptInfo.PurchaseDateTime)
    end)
    
    if not success then
        print("Error: " .. tostring(error))
        return Enum.ProductPurchaseDecision.NotProcessedYet
    end
    
    -- If the player is valid 
    local productId = receiptInfo.ProductId        
    local userId = receiptInfo.PlayerId
    
    local player = Players:GetPlayerByUserId(userId)  
    if player == nil then
        print("Error: player is nil")
        return Enum.ProductPurchaseDecision.NotProcessedYet	
    end  
    
    -- Trigger the product delivery function
    local delivererFunc = ProductDeliverer[productId]
    local success, error = delivererFunc(player)
    
    -- If the product is successfully delivered
    if success then
        -- Return the status as delivered
        print("Item delivery successful / ProductId : " .. productId)
        return Enum.ProductPurchaseDecision.PurchaseGranted
        
    -- If product delivery fails
    else
        print("Error: " .. tostring(error))
        return Enum.ProductPurchaseDecision.NotProcessedYet
    end
end
MarketplaceService.ProcessReceipt = OnProcessReceipt
```

## See also

{% content-ref url="/pages/9Hau8mThHVEcQwyAQ829" %}
[World Product Sales](/manual/monetization/marketplace.md)
{% endcontent-ref %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.overdare.com/development/api-reference/classes/marketplaceservice.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
