For my game I wanted to make a chat tag for those who donated, I have 7 dev products that can give that chat tag. The file I’ve attached below is the Developer & tester tag I edited, I’m just unsure how to make something similar to this that works for the donators. I don’t have any experience with the dev product system.
To do this you would have to use a purchase history table alongside all the other values you are saving to your datastore for each player. When a player purchases something, you should add the receipt id and some details about the product into the table (key is receipt id, details is the value). When a player loads into the game, you need to loop through this table and check if any of the product IDs match with the donation product IDs (would be in the details stored), and if so, set an attribute for the player to say that they have donated. So in the chat tag method, you can check this value and add a donation tag accordingly.
You don’t have to use a purchase history table, but I’m giving you a solution using one because it is good practise to use purchase history tables regardless, and this is to help prevent session locking. You can instead just save a value for the player that indicates if they have donated, and this can be set in the process receipt function depending on the product id of the purchase. And of course load this value and use it to add a chat tag if need be.
I’m assuming you mean something like this? How would I go about adding the chat tag to this, and does this require a different script/location or is the local script in StarterPlayerScripts still a good place for it?
Yes exactly, however, this code should be on the server side instead. Also, rember to set the player product key value into the purchase history data store before you return purchase granted. You can use receiptInfo.ProductId to get the product id, and then you can simply use if devProducts[receiptInfo.ProductId] to determine wether the purchase was a donation (assuming all those ids in the table are donation products), and if so, add a new attribute to the player, call it ‘donated’ and set it to true. Back in the chat tag script, just use if player:GetAttribute(‘Donated’) to add the custom donation chat tag.
I’m a little bit confused, again I have no experience with dev products so I don’t really know what goes where in the script. The donations are the only dev products in the game so would the table be unnecessary in this case? I also don’t know where the attribute would go in the script without getting errors.
here’s the client script that would work with a ChatTag attribute on the player:
-- script by bestspyboy/devforum
local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")
TextChatService.OnIncomingMessage = function(message)
local props = Instance.new("TextChatMessageProperties")
local tagString = ""
if message.TextSource then
-- get the player that sent the message
local player = Players:GetPlayerByUserId(message.TextSource.UserId)
if player then
local tagText = player:GetAttribute("ChatTag")
tagString = tagText -- or add some text, or whatever you need to do
end
end
props.PrefixText = tagString .. message.PrefixText
return props
end
also that processReceipt has to be on a server script, as does the datastore code. looks like you’re already storing the player’s history so you can just add this to where you handle the ProcessReceipt:
-- at top of file
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")
-- ...
-- where you process the payment
donatorStore:SetAsync(receiptInfo.PlayerId)
and create a new server script:
-- script by bestspyboy/devforum
local Players = game:GetService("Players")
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")
Players.PlayerAdded(function (player)
local success, error = pcall(function()
purchased = donatorStore:GetAsync(player.UserId)
end)
if success and purchased then -- the player has donated before
player:SetAttribute("ChatTag", "<font>Donator</font>")
elseif error then
warn("DataStore error: " .. error)
end
end)
this also means that your existing logic for checking if a user is a developer can be moved to the server if you want. it makes no real difference for precoded values.
you can’t have two TextChatService.OnIncomingMessage scripts if that’s what’s breaking, you’ll have to disable your old script for this to work (you can move things across if you want!)
if there’s an error message when the game loads, please send it :D
looks like there’s no end for the end of that processReceipt function. could be just the screenshot, but if there’s no end you need to finish the function, and add the donatorStore:SetAsync line.
it’s a linting problem (an accidental global variable), not an error, but you can add this on the line before the pcall if you want.
--LocalScript
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local tcs = game:GetService("TextChatService")
local devProducts = {"1898110950", "1898111128", "1898111346", "1898111537", "1898111705", "1898111875", "1898112079"}
-- Data store for tracking purchases that were successfully processed
local purchaseHistoryStore = DataStoreService:GetDataStore("PurchaseHistory")
local function processReceipt(receiptInfo)
-- Determine if the product was already granted by checking the data store
donatorStore:SetAsync(receiptInfo.PlayerId)
local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
local purchased = false
local success, result, errorMessage
success, errorMessage = pcall(function()
purchased = purchaseHistoryStore:GetAsync(playerProductKey)
end)
-- If purchase was recorded, the product was already granted
if success and purchased then
return Enum.ProductPurchaseDecision.PurchaseGranted
elseif not success then
error("Data store error:" .. errorMessage)
end)
i’ve cleaned up the code, fixed the formatting and added the required code to save the user’s donation. copy this whole thing and replace the existing script.
-- edited by bestspyboy/devforum
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local tcs = game:GetService("TextChatService")
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")
-- you don't need this line, keeping in case you do something else
local devProducts = {"1898110950", "1898111128", "1898111346", "1898111537", "1898111705", "1898111875", "1898112079"}
-- Data store for tracking purchases that were successfully processed
local purchaseHistoryStore = DataStoreService:GetDataStore("PurchaseHistory")
local function processReceipt(receiptInfo)
-- Determine if the product was already granted by checking the data store
local playerProductKey = receiptInfo.PlayerId .. "_" .. receiptInfo.PurchaseId
local purchased = false
local success, result, errorMessage
success, errorMessage = pcall(function()
purchased = purchaseHistoryStore:GetAsync(playerProductKey)
end)
-- If purchase was recorded, the product was already granted
if success and purchased then
return Enum.ProductPurchaseDecision.PurchaseGranted
elseif not success then
error("Data store error:" .. errorMessage)
return Enum.ProductPurchaseDecision.NotProcessedYet
end
local success2, errorMessage2 = pcall(function()
purchased = donatorStore:SetAsync(receiptInfo.PlayerId, true) -- store the user as a donator
end)
if not success2 then
error("Failed to process receipt due to data store error: " .. errorMessage2)
return Enum.ProductPurchaseDecision.NotProcessedYet
else
-- IMPORTANT: Tell Roblox that the game successfully handled the purchase
return Enum.ProductPurchaseDecision.PurchaseGranted
end
end
MarketplaceService.ProcessReceipt = processReceipt
This one also appears to have an issue, I removed the developer tag to see if the donation one worked (the previous script was disabled and moved to workspace), this one seems to break the chat. Only when the developer tag code is removed though.
-- script by bestspyboy/devforum
local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")
TextChatService.OnIncomingMessage = function(message)
local props = Instance.new("TextChatMessageProperties")
local tagString = ""
if message.TextSource then
-- get the player that sent the message
local player = Players:GetPlayerByUserId(message.TextSource.UserId)
if player then
local tagText = player:GetAttribute("ChatTag")
tagString = tagText -- or add some text, or whatever you need to do
end
end
props.PrefixText = tagString .. message.PrefixText
return props
end
local tcs = game:GetService("TextChatService")
local players = game:GetService("Players")
local developers = {'YTFuntimeFoxyIsPro'}
local testers = {'CarsBoi101'}
--process a message
local function addTag(message:TextChatMessage): TextChatMessageProperties|nil
--create new properties
local properties = Instance.new("TextChatMessageProperties")
if not message.TextSource then return nil end
local player = players:GetPlayerByUserId(message.TextSource.UserId)
--developer tag as example
if table.find(developers, player.Name) then
properties.PrefixText = "<font color = \"rgb(0, 251, 255)\">[Developer]</font> " .. message.PrefixText
elseif
table.find(testers, player.Name) then
properties.PrefixText = "<font color = \"rgb(255, 0, 0)\">[Game Tester]</font> " .. message.PrefixText
end
return properties --return the properties to be used
end
--set the callback - this can only be done once
tcs.OnIncomingMessage = addTag
this is registering TextChatService.OnIncomingMessage twice. you can only do that once. you’ll have to merge the scripts like below - don’t try to combine anything. this is a complete script. you don’t have to edit anything, just paste it exactly like this.
-- script by bestspyboy/devforum
local Players = game:GetService("Players")
local TextChatService = game:GetService("TextChatService")
local developers = {'YTFuntimeFoxyIsPro'}
local testers = {'CarsBoi101'}
-- check if the user is a developer or tester
local function getDevTag(player)
if table.find(developers, player.Name) then
return "<font color = \"rgb(0, 251, 255)\">[Developer]</font> " .. message.PrefixText
elseif table.find(testers, player.Name) then
return "<font color = \"rgb(255, 0, 0)\">[Game Tester]</font> " .. message.PrefixText
else
return false
end
end
TextChatService.OnIncomingMessage = function(message)
local props = Instance.new("TextChatMessageProperties")
local tagString = ""
if message.TextSource then
-- get the player that sent the message
local player = Players:GetPlayerByUserId(message.TextSource.UserId)
if player then
-- check if the player is a developer/tester
local devTag = getDevTag(player)
-- check if the player has another assigned ChatTag from server
local tagText = player:GetAttribute("ChatTag")
if devTag then
tagString = devTag
elseif tagText then
tagString = tagText
else
-- no chat tag, empty string
tagString = ""
end
end
end
-- add the tagString to the message
props.PrefixText = tagString .. message.PrefixText
return props
end
Okay, I didn’t realize that. This is the only script giving me issues now
-- script by bestspyboy/devforum
local Players = game:GetService("Players")
local donatorStore = game:GetService("DataStoreService"):GetDataStore("DonatorStore")
Players.PlayerAdded(function (player)
purchased = donatorStore:GetAsync(player.UserId)
local success, error = pcall(function()
end)
if success and purchased then -- the player has donated before
player:SetAttribute("ChatTag", "<font color = \"rgb(128, 0, 255)\">[Donator]</font> ")
elseif error then
warn("DataStore error: " .. error)
end
end)