As opposed to purchases for bundles and assets, the PromptBulkPurchase finished must be called from the client and I have no idea why.
But it can be easily exploited, if an user wanted to award money or in-game rewards, making a remote event which would fire to the server in order to grant rewards.
They can fire a remote event, with correct details and there you go. It’s enough to fire the table, asset ids, robux spent and everything which the documentation states.
I’ve used PlayerOwnsAsset to check whether they own the assets or not, and that is an okay solution regarding this, but it would’ve been easier if there was just a server side check built in.
One of my solutions regarding this was to make a receipt system which would generate a unique identifier of the purchase, then send it back to the client and forward it to the server.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local GenerateReceiptID = ReplicatedStorage:WaitForChild("GenerateReceiptID")
local forwardReceipt = ReplicatedStorage:WaitForChild("ForwardReceipt")
local activeTransactions = {} -- Stores active transactions with their IDs
local receiptIdSet = {} -- Set to ensure uniqueness of receipt IDs
-- Function to generate a unique receipt ID
local function generateUniqueReceiptID()
local chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
local length = 16
local receiptId = ''
for i = 1, length do
local randIndex = math.random(1, #chars)
receiptId = receiptId .. chars:sub(randIndex, randIndex)
end
return receiptId
end
-- RemoteFunction to get a receipt ID
GenerateReceiptID.OnServerInvoke = function(player)
local receiptId
repeat
receiptId = generateUniqueReceiptID()
until not receiptIdSet[receiptId] -- Ensure uniqueness
receiptIdSet[receiptId] = true
-- Notify relevant servers about the receipt ID
forwardReceipt:Fire(player, receiptId)
wait(1)
return receiptId
end
This way, the server gets the receipt first, then the client, which will fire it over to the server when Bulk purchase is completed.
I made a table to store valid receipts and as soon as it is processed on the other server script, the receipt is removed.
Exploits can not break the wall since the other server script requires the receipt as well, and only takes valid receipts from this server script - not from client.
I do not have amazing understanding of scripting and yes, this can be improved, but this is my method against exploiting bulk purchases for now.