--[[ Buy items ]]--
function itemBuy(player, itemType, item, currency, cost)
if player and currency and cost then
local myData = playerData[player.UserId]
if myData then
if currency == 'Cash' then
--[[ Cash purchase ]]--
if myData.cash >= cost then
updateCash:Fire(player, -cost)
purchase(player, itemType, item) -- For character items
return true
else
-- Not enough cash to buy
return false
end
else
--[[ Robux purchase ]]--
marketplaceService:PromptProductPurchase(player, cost) -- Cost == ProductId
local purchaseHistory = dataStoreService:GetDataStore('PurchaseHistoryTest')
function marketplaceService.ProcessReceipt(receiptInfo)
local playerProductKey = receiptInfo.PlayerId .. ':' .. receiptInfo.PurchaseId
if purchaseHistory:GetAsync(playerProductKey) then
return Enum.ProductPurchaseDecision.PurchaseGranted
end
if not game:GetService('Players'):GetPlayerByUserId(receiptInfo.PlayerId) then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
if receiptInfo.ProductId == cost then
purchase(player, itemType, item)
return true
end
purchaseHistory:SetAsync(playerProductKey, true)
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
end
end
end
buyItem.OnServerInvoke = itemBuy
As you can see I’m trying to use a RemoteFunction, that fires whenever a player clicks buy, and if they purchase the item it returns true. Problem with Robux purchases is now you have to use ‘ProcessReceipt’ which seems like a lot more work than what was needed before (PromptProductPurchaseFinished) and with all this data storing which makes no sense whatsoever. All I want is to check if the player has purchased a developer product, return true, so the LocalScript can continue with what it wants to do. Problem is, according to the wiki, the ProcessReceipt script needs like half a dozen returns, which messes with my main return.
In short I have no clue what a single line of the ProcessReceipt does. I just want something to fire when a player buys a product. No saving any of it to datastores or whatever.
EDIT
And can anyone explain why this is only for dev products? It’s very easy to check if someone has purchased a gamepass or asset with just a line. Why do dev products need to be so long and over complicated all of a sudden?
Dev products are extremely flexible and as such, implementation is completely up to the developer. They are only purchasable in-game so you need to handle the purchase in-game. If you fail to do so then the ProcessReceipt function will be called every time the player joins the game in future.
You need to define the ProcessReceipt function in the main scope of your script, before you prompt purchase. Your ProcessReceipt function should be set up in such a way that it can handle and verify old purchases from previous sessions marked as “NotProcessedYet” as well as new ones in the current session.
If you are trying to get a return value of true or false and you want your script to “wait” for that response, you’ll need something along the following lines:
local Players = game:GetService( 'Players' )
local RunService = game:GetService( 'RunService' )
local PurchaseHistory = game:GetService( 'DataStoreService' ):GetDataStore( 'PurchaseHistory' )
local pendingPurchases = {}
function marketplaceService.ProcessReceipt( receiptInfo )
-- For data storage purposes, and handling purchases that couldn't be caught first time round
local playerProductKey = receiptInfo.PlayerId .. ':' .. receiptInfo.PurchaseId
if PurchaseHistory:GetAsync( playerProductKey ) then
return Enum.ProductPurchaseDecision.PurchaseGranted
end
local player = Players:GetPlayerByUserId( receiptInfo.PlayerId )
if not player then
return Enum.ProductPurchaseDecision.NotProcessedYet
end
PurchaseHistory:SetAsync( playerProductKey, true )
return Enum.ProductPurchaseDecision.PurchaseGranted
end
function triggerPurchase( player, cost )
-- Triggering a purchase
local userId = player.UserId
if not pendingPurchases[ userId ] then
pendingPurchases[ userId ] = {}
end
pendingPurchases[ userId ][ cost ] = nil
marketplaceService:PromptProductPurchase( player, cost )
repeat
RunService.Stepped:Wait()
until typeof( pendingPurchases[ userId ][ cost ] ) == 'boolean' ) or not Players:GetPlayerByUserId( userId )
return pendingPurchases[ userId ] and pendingPurchases[ userId ][ cost ]
end
marketplaceService.PromptProductPurchaseFinished:Connect( function ( userId, productId, isPurchased )
-- For detecting purchase status within the current session
if pendingPurchases[ userId ] then
pendingPurchases[ userId ][ productId ] = isPurchased
end
end )
Players.PlayerRemoving:Connect( function ( player )
if pendingPurchases[ player.UserId ] then
pendingPurchases[ player.UserId ] = nil
end
end )
-- Trigger the purchase:
local purchaseCompleted = triggerPurchase( player, cost )
-- returns true, false or nil based on success, failure or player left
So after triggering a purchase it waits until a response is obtained or the player has left.
Please note that ProcessReceipt is not something new. You need to focus on the difference between dev products and gamepasses. From looking at your code you’d be better off with gamepasses as it seems like you only want a one-time sale. Dev products are for infinite sales of something that you want to record in-game.
They’ve always been this “long and over complicated” and if you haven’t been doing this in the past then it’s possible that you’ve been receiving purchases without handling them correctly or not receiving the robux you should by not implementing it correctly.
I believe that it’d probably be easier for you to just use DataStore2’s method (backups) instead of storing puchases, although how well it’ll work will depend on how well you make it work.