Описанные ранее способы монетизации конечно работают. Но вот пришла беда, откуда не ждали! Они «теряются» если персонаж умирает! Или когда игрок жмёт «Reset character». Вот уж действительно — нежданчик.

Пришлось пересмотреть подход к сохранению информации о покупки конкретным игроком.
Во первых данная информация должна хранится на сервере.
Во вторых данная информация должна загружаться после смерти персонажа.
В третьих там должна храниться информация и о временных покупках, если они накладывают какой-то эффект на самого персонажа.
Варианты были разные. Но у всех были какие-либо недостатки и тут меня осенило! Единственное что не изменяется пока игрок не вышел это сам игрок! Провёл небольшое тестирование и вот оно — решение найдено = нужно хранить данные о покупках в переменных, которые будут лежать в самом игроке!

Но, так как при входе игрока этих переменных нет, то их надо создать. А также нужно их читать, когда игрок воскрешается.
Для начала, переделал код для GamePass-а:
--[[
https://developer.roblox.com/en-us/api-reference/event/Player/CharacterAdded
https://developer.roblox.com/en-us/api-reference/event/Player/CharacterRemoving/index.html
https://developer.roblox.com/en-us/api-reference/property/Player/Character/index.html
https://developer.roblox.com/en-us/api-reference/event/IntValue/Changed
]]--
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local tmp
local id1 = 8626045 -- id gamepass - 5s
-- ищем игрока
-- добавляем ему флаг, что оно куплено
-- убираем с экрана возможность покупки
local function buy(tmp)
	local plr = game.Players:FindFirstChild(tmp)
	local char =  plr.Character:WaitForChild("HumanoidRootPart")
	RunService.Stepped:wait()
	char = plr.Character
	if char ~= nil then -- игрок не покинул игру
		if game:GetService("MarketplaceService"):UserOwnsGamePassAsync(game.Players[char.Name].UserId, id1) then
			-- что тут нужно сделать?!			
			char.Humanoid.WalkSpeed = 30
			
			if plr:FindFirstChild("Buy") == nil then -- проверяем наличие переменной, если нет - создаём
				local Buy = Instance.new("IntValue")
				Buy.Parent = plr
				Buy.Name = "Buy"
				Buy.Value = 5
			end		
			-- а теперь убираем Frame покупки
			local pl = game.Players:FindFirstChild(plr.Name)
			if pl ~= nil then
				pl.PlayerGui.Main.GamePass.Visible=false
				pl.PlayerGui.Main.Answer.Visible=true
			end			
		end
	end	
end
-- проверка уже приобретённого пропуска при подключении игрока
game.Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:connect(function(char)
		buy(char.Name)
	end)
    plr.CharacterRemoving:Connect(function(char)		
		buy(char.Name)
		local pl = plr -- Players:GetPlayerFromCharacter(char)
		if pl:FindFirstChild("Buy") ~= nil then -- проверяем наличие переменной
			if pl.Buy.Value == 3 then
				pl.PlayerGui.Main.GameDev.Visible=false
				pl.PlayerGui.Main.Answer.Visible=true
			end
		end
    end)
end)
-- проверка на совершении покупки
game:GetService("MarketplaceService").PromptGamePassPurchaseFinished:Connect(function(plr,ido,purchased)
	if purchased and ido == id1 then -- пропуск с id1
		-- local char = plr.Character
		buy(plr.Name)
	end
end)
Как можно заметить, я немножко оптимизировал. Вынес код необходимых действий из блоков на вход игрока и покупку игроком GamePass в самой игре в отдельный блок, которому в качестве параметра просто передаётся имя игрока.
В подключении игрока теперь не только создание чара — CharacterAdded, но и его удаление — CharacterRemoving. Это удаление и происходит при смерти персонажа или когда нажата вышеупомянутая кнопка.
Так же добавил «уборку» с экрана возможности покупки GamePass-а, если он у нас уже куплен. Нечего ему глаза мозолить.
Так как у GamePass-а значение 5 чуть выше, чем у временной покупки — 3 , то обошёлся одной переменной.
local MS = game:GetService("MarketplaceService") -- сокращение для вызова сервиса
local function processReceipt(ReceiptInfo)
	local player = game:GetService("Players"):GetPlayerByUserId(ReceiptInfo.PlayerId) -- получаем Id игрока
	
	if not player then -- если не игрок, то вылетаем
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end
	
	-- если игрок найден, выполняем действия
	-- print(ReceiptInfo.PlayerId .. " buy " .. ReceiptInfo.ProductId)	
	local char=player.Character
	if player:FindFirstChild("Buy") == nil then -- проверяем наличие переменной, если нет - создаём
		local Buy = Instance.new("IntValue")
		Buy.Parent = player
		Buy.Name = "Buy"
		Buy.Value=3
	else
		player.Buy.Value = 3
	end
	char.Humanoid.WalkSpeed = 30
	-- а теперь убираем Frame покупки
	local pl = game.Players:FindFirstChild(player.Name)
	if pl ~= nil then
		pl.PlayerGui.Main.GameDev.Visible=false
		pl.PlayerGui.Main.Answer.Visible=true
	end
	
	-- возвращаем, что покупка произведена
	return Enum.ProductPurchaseDecision.PurchaseGranted
	
end
MS.ProcessReceipt= processReceipt
Как видим, всё оказалось не так уж и страшно, если знать что искать.
Остальные скрипты (для кнопок) остались без изменений.
И как всегда, результат можно увидеть и опробовать на странице арифметического тренажёра:
https://www.roblox.com/games/4789977549/Math-Rocket