MarketplaceService

MarketplaceService : Instance

Overview

MarketplaceService는 월드 내에서의 상품 구매 및 결제 성공에 따른 지급 처리를 담당하는 서비스입니다.

크리에이터는 상품이 판매되었을 때 지급이 누락되지 않도록 반드시 ProcessReceipt 콜백 함수를 정의해야 하며, DataStore 등을 활용하여 정상적으로 지급이 완료되도록 처리할 책임이 있습니다.

Properties

Methods

GetProductInfo

상품 ID(productId)와 상품 타입(Enum.InfoType)에 해당하는 상품 정보를 반환합니다.

Parameters

number ProductId

상품의 ID입니다.

Enum.InfoType InfoType

상품의 타입입니다.

Return

Dictionary

상품 정보로 구성된 딕셔너리입니다.

  • string Name: 상품 이름

  • string Description: 상품 설명

  • number ProductId: 상품 ID

  • string ProductType: 상품 종류

  • number PriceInBLUC: 상품 가격

  • number Created: 상품이 생성된 시간 (UNIX timestamp)

  • number Updated: 상품이 수정된 시간 (UNIX timestamp)

Code Samples

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

모든 월드 상품에 대한 정보를 포함하는 Pages 객체를 반환합니다.

Parameters

Return

Pages

현재 월드의 모든 월드 상품에 대한 정보를 포함하는 객체입니다.

Code Samples

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()	
		    
            -- 마지막 페이지이면 루프 탈출 
            if worldProducts.IsFinished or currentPage == nil then           	
                print(pageCount .. " page IsFinished : " .. tostring(worldProducts.IsFinished))
                break
            else
                worldProducts:AdvanceToNextPageAsync()
                pageCount = pageCount + 1
            end
	    
            -- 한 페이지에 최대 100개의 상품 정보 구성
            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

월드 상품 ID(productId)에 해당하는 상품의 구매를 요청합니다. (시스템 UI로 구매창이 출력됩니다.)

Parameters

Player Player

상품을 구매할 Player입니다.

number ProductId

월드의 상품 Id입니다.

Return

void

Code Samples

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

구매 요청(PromptProductPurchase)을 통해 출력된 구매창이 꺼질 때 이벤트가 호출되며, 구매를 성공하면 isPurchased에 true가 전달되고, 구매를 취소하거나 실패하면 false가 전달됩니다.

이 이벤트는 구매 창을 닫았는지 감지하기 위한 용도로만 사용해야 하며, 구매한 상품에 대한 지급 처리 용도로는 절대 사용하지 않아야 합니다.

Parameters

string UserId

구매 요청한 Player의 UserId입니다.

number ProductId

구매 요청한 상품의 Id입니다.

bool bIsPurchased

구매 성공 여부입니다.

Code Samples

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

구매 성공한 상품 중에서 아직 지급 처리가 되지 않은 영수증 정보를 반환하는 이벤트가 호출됩니다.

호출 조건

  • 월드 상품을 성공적으로 구매했을 때(구매 성공 팝업이 사용자에게 표시되었을 때)

    • 미처리 상품이 있는 상태에서 새로운 상품 구매시, 이전 미처리건도 함께 호출됩니다.

  • 사용자가 서버에 접속(재접속)했을 때

지급 상태 변경 방법

  • 상품 지급 처리 후에, Enum.ProductPurchaseDecision.PurchaseGranted를 반환합니다.

주의사항

  • ProcessReceipt 이벤트 연결은 서버측 Script에서 한 번만 설정해야 합니다.

  • 이 콜백은 시간 제한 없이 yield 가능하며, 서버가 실행 중인 한 응답이 돌아올 때까지 유효합니다.

  • 미지급된 영수증이 여러개인 경우 각각 호출되며, 콜백 호출 순서는 비결정적(non-deterministic)입니다.

  • 사용자가 서버에 있어야 콜백이 호출됩니다.

    • 단, 콜백의 결과는 사용자가 서버에 없어도 백엔드에 기록될 수 있습니다.

  • 콜백에서 PurchaseGranted를 반환해도 백엔드 기록이 실패할 수 있으며, 이런 경우 영수증의 상태는 변경되지 않습니다. (미지급 상태 유지)

  • 미지급 상태의 상품은 자금이 지급 보류 상태(Escrow)로 보관됩니다.

Parameters

Dictionary Receipt

구매 성공한 상품의 영수증 정보로 구성된 딕셔너리입니다.

  • string PurchaseId: 영수증 Id

  • string PlayerId: Player의 UserId

  • number ProductId: 상품 Id

  • number CurrencySpent: 거래에 사용된 화폐의 양

  • number PurchaseDateTime: 상품을 결제한시간 (UNIX timestamp)

Return

Enum.ProductPurchaseDecision

월드 상품의 지급 상태입니다.

  • PurchaseGranted: 상품이 Player에게 성공적으로 지급된 상태

  • NotProcessedYet: 상품이 지급되지 않은 상태

Code Samples

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

local ProductDeliverer = {}

-----------------------------------------------------------------------------
-- 아래와 같이 테이블에 상품 번호 별로 함수를 구성하여 상품마다 지급 로직을 구현할 수 있습니다.
ProductDeliverer[Enter the product number here.] = function(player)
    local success, resultOrError = pcall(function()
        -- player에게 상품 지급 및 DataStore를 이용한 저장 처리 
        
        -- Tip.
        -- DataStore로 상품 정보를 저장할 때는 
        -- 네트워크 충돌이나 경쟁 상태(race condition)를 방지하기 위해
        -- IncrementAsync 또는 UpdateAsync로 처리하는 것을 권장합니다.
                
        -- 지급 및 저장 처리가 성공적으로 완료된 경우 true 반환
        return true
    end)
    
    if success and resultOrError then
        return true
        
    else
        return false, resultOrError
    end
end

-----------------------------------------------------------------------------
-- 상품 구매 성공시 호출되는 영수증 처리용 콜백
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
    
    -- 플레이어가 유효하면 
    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  
    
    -- 상품 지급 함수 호출
    local delivererFunc = ProductDeliverer[productId]
    local success, error = delivererFunc(player)
    
    -- 상품 지급 성공시
    if success then
        -- 지급 완료 상태 반환
        print("Item delivery successful / ProductId : " .. productId)
        return Enum.ProductPurchaseDecision.PurchaseGranted
        
    -- 상품 지급 실패시
    else
        print("Error: " .. tostring(error))
        return Enum.ProductPurchaseDecision.NotProcessedYet
    end
end
MarketplaceService.ProcessReceipt = OnProcessReceipt

See also

월드 상품 판매

Last updated