Full Code
--!strict
local GamepassReceipt = {}
GamepassReceipt.__index = GamepassReceipt
--[[
GamepassReceipt:PromptGamepassPurchase(player: Player, gamepassId: number): boolean
GamepassReceipt:OnGamepassPurchase(function(player: Player, gamepassInfo: GamepassInfo, isPurchased: boolean)
if isPurchased then
print(gamepassInfo) -- Return a gamepass info in table format | MarketplaceService:GetProductInfo(gamepassId, Enum.InfoType.GamePass)
else
print(player.Name .. " did not purchase gamepass with ID: " .. gamepassId)
end
return response
end)
GamepassReceipt:SaveGamepassArray({}) -- Saves the gamepass array to a persistent storage
:andThen(function()
print("Gamepass array saved successfully.")
end)
:andCatch(function(error)
warn("Failed to save gamepass array: [", error .. "]")
end)
GamepassReceipt:TrackPurchases(enable: boolean) -- Enables or disables tracking of gamepass purchases
GamepassReceipt:GetPlayerHistory(playerUserId: number) -- Returns a table of gamepass purchase history for the player
-- Example: { {gamepassId = 123456, purchaseDate = "2023-10-01"}, ... }
GamepassReceipt:HasGamepass(playerUserId: number, gamepassId: number): boolean
-- Returns true if the player has the gamepass, false otherwise
GamepassReceipt:SetAliasGamepassId(alias: string, gamepassId: number)
-- Sets an alias for a gamepass ID, allowing you to use the alias instead of the ID in other methods
GamepassReceipt:PromptByAlias(player: Player, alias: string): boolean
-- Prompts the player to purchase a gamepass using an alias instead of the gamepass ID
GamepassReceipt:CleanHistoryPlayer(playerUserId: number)
-- Cleans the purchase history for the player, removing all entries
GamepassReceipt.CanChangePrice: boolean -- Allows changing gamepass prices for testing purposes
VERSION = "1.0.0",
DESCRIPTION = "Gamepass Receipt Module",
AUTHOR = "ImNotServi",
LICENSE = "MIT",
--]]
-->> Services
local MarketplaceService = game:GetService("MarketplaceService")
local DataStoreService = game:GetService("DataStoreService")
local RunService = game:GetService("RunService")
GamepassReceipt.__Prefix = "[GPReceipt] "
GamepassReceipt.__History = {}
GamepassReceipt.__Aliases = {}
GamepassReceipt.TrackingEnabled = false
GamepassReceipt.CanChangePrice = false
GamepassReceipt.DebugMode = false
GamepassReceipt.__Profiles = {}
GamepassReceipt.__TemporalDataSaved = {}
GamepassReceipt.__StorageGamepass = {}
GamepassReceipt.__db = {}
GamepassReceipt.__DSName = "GamepassReceiptDataStore"
-->> Methods
--//Clean history player
function GamepassReceipt:CleanHistoryPlayer(playerUserId: number)
if RunService:IsClient() then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "CleanHistoryPlayer cannot be called from the client.")
end
return
end
if self.TrackingEnabled then
if self.__Profiles[playerUserId] then
self.__Profiles[playerUserId] = {}
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "History cleaned for player: " .. tostring(playerUserId))
end
end
else
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "Tracking is not enabled. Cannot clean player history.")
end
end
end
--//Save data player
function GamepassReceipt:SaveHistory(playerUserId: number)
if RunService:IsClient() then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "SaveHistory cannot be called from the client.")
end
return
end
if self.TrackingEnabled then
local getProfile = self.__Profiles[playerUserId]
if getProfile == nil then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "Player profile not found for saving history.")
end
return
end
if #getProfile <= 0 then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "No gamepass history to save for player: " .. tostring(playerUserId))
end
return
end
local success, result = pcall(function()
local dataStore = DataStoreService:GetDataStore(self.__DSName)
return dataStore:SetAsync(tostring(playerUserId), getProfile)
end)
if not success then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "Failed to save player history: " .. tostring(result))
end
end
else
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "Tracking is not enabled. Cannot save player history.")
end
end
end
--// get history player
function GamepassReceipt:GetPlayerHistory(playerUserId: number)
if RunService:IsClient() then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "GetPlayerHistory cannot be called from the client.")
end
return {}
end
if not self.TrackingEnabled then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "Tracking is not enabled. Cannot get player history.")
end
return {}
end
if GamepassReceipt.__Profiles[playerUserId] == nil then
if GamepassReceipt.__db[playerUserId] then
return {}
end
GamepassReceipt.__db[playerUserId] = true
local success, result = pcall(function()
local dataStore = DataStoreService:GetDataStore(self.__DSName)
return dataStore:GetAsync(tostring(playerUserId))
end)
if success and result then
GamepassReceipt.__Profiles[playerUserId] = result
GamepassReceipt.__db[playerUserId] = false
return result or {}
else
GamepassReceipt.__Profiles[playerUserId] = {}
GamepassReceipt.__db[playerUserId] = false
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "Failed to get player history")
end
return {}
end
else
return GamepassReceipt.__Profiles[playerUserId] or {}
end
end
--// PROMPT BY ALIAS
function GamepassReceipt:HasGamepass(playerUserId: number, gamepassId: number): boolean
local success, result = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(playerUserId, gamepassId)
end)
if success then
return result
else
if GamepassReceipt.DebugMode then
warn(GamepassReceipt.__Prefix .. "Failed to check if player has gamepass ID: " .. tostring(gamepassId))
end
return false
end
end
--// Prompt by alias
function GamepassReceipt:PromptByAlias(player: Player, alias: string)
local response = {
_success = false,
_error = false,
_errorMessage = "nil",
}
function response:andCatch(callback)
if response._error then
callback(self._errorMessage)
end
return self
end
function response:andThen(callback)
if self._success then
callback()
end
return self
end
-- Check if the alias exists
if not self.__Aliases[alias] then
response._error = true
response._errorMessage = "Alias does not exist."
return response
end
local gamepassId = self.__Aliases[alias]
if GamepassReceipt.TrackingEnabled then
if GamepassReceipt.__Profiles[player.UserId] == nil then
response._error = true
response._errorMessage = "Player profile not found"
return response
end
end
-- Si ya fue guardado algo antes
if GamepassReceipt.__TemporalDataSaved[player.UserId] then
response._error = true
response._errorMessage = "Player already prompted or pending"
return response -- This will prevent multiple prompts for the same player
end
-- Verificación y cache temporal
if GamepassReceipt.__TemporalDataSaved[player.UserId] == nil then
GamepassReceipt.__TemporalDataSaved[player.UserId] = {}
end
-- Intentar obtener la información del producto
local success, result = pcall(function()
return MarketplaceService:GetProductInfo(gamepassId, Enum.InfoType.GamePass)
end)
if success and result then
GamepassReceipt.__TemporalDataSaved[player.UserId] = result
MarketplaceService:PromptGamePassPurchase(player, gamepassId)
response._success = true
return response -- successfully prompted the player
end
if success and not result then
local msg = "Gamepass with ID " .. gamepassId .. " does not exist or is invalid."
response._error = true
response._errorMessage = msg
return response -- This will catch any other errors that may occur
end
if not success then
local msg = "Failed to get product info for gamepass ID " .. gamepassId .. ": " .. tostring(result)
response._error = true
response._errorMessage = msg
return response -- This will catch any other errors that may occur
end
-- Error handling
response._error = true
response._errorMessage = "Unknown error"
return response
end
--// ALIAS GAMEPASS ID
function GamepassReceipt:SetAliasGamepassId(alias: string, gamepassId: number)
local response = {
_success = false,
_error = "nil",
}
function response:andCatch(callback)
if response._error then
callback(response._error)
end
return response
end
function response:andThen(callback)
if response._success then
callback()
end
return response
end
if RunService:IsClient() then
if self.DebugMode then
warn(self.__Prefix or "Unknown" .. "SetAliasGamepassId cannot be called from the client.")
end
return response
end
-- check if the alias has space
if string.find(alias, " ") then
response._error = "Alias cannot contain spaces."
return response
end
-- check if gamepass id has space
if string.find(tostring(gamepassId), " ") then
response._error = "Gamepass ID cannot contain spaces."
return response
end
if type(alias) ~= "string" or type(gamepassId) ~= "number" then
response._error = "Alias must be a string and gamepassId must be a number."
return response
end
self.__Aliases[alias] = gamepassId
response._success = true
return response
end
--// Storage a new gamepass receipt
function GamepassReceipt:SaveGamepassArray(gamepassArray)
local response = {
_success = false,
_error = nil,
_errorMessage = "nil",
}
function response:andCatch(callback)
if response._error then
callback(response._errorMessage)
end
return response
end
function response:andThen(callback)
if response._success then
callback()
end
return response
end
if RunService:IsClient() then
warn(self.__Prefix or "Unknown" .. "SaveGamepassArray cannot be called from the client.")
response._success = true
response._errorMessage = "SaveGamepassArray cannot be called from the client."
return response
end
--[[
if type(gamepassArray) ~= "table" then
response._error = "Gamepass array must be a table."
return response
end
]]
--self.__StorageGamepass = self.__StorageGamepass or {}
for _, gamepass in ipairs(gamepassArray) do
if table.find(self.__StorageGamepass, gamepass) then
response._success = true
response._errorMessage = "Gamepass already exists in storage: " .. tostring(gamepass)
return response
end
end
-- Si llegamos aquÃ, todo está bien, insertamos los nuevos
for _, gamepass in ipairs(gamepassArray) do
table.insert(self.__StorageGamepass, gamepass)
end
response._success = true
return response
end
--// Finished gamepass purchase
function GamepassReceipt:OnGamepassPurchase(callback)
local response = {
_catchers = {},
_error = false,
_errorMessage = "nil",
}
function response:andCatch(catchCallback)
if typeof(catchCallback) == "function" then
table.insert(self._catchers, catchCallback)
if self._error then
task.defer(catchCallback, self._errorMessage)
end
end
return self
end
if typeof(callback) ~= "function" then
response._error = true
response._errorMessage = "Callback function is required and must be a function."
for _, fn in ipairs(response._catchers) do
fn(response._errorMessage)
end
return response
end
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, gamepassId, isPurchased)
if not isPurchased then
callback(player, gamepassId, false, nil)
return
end
local findGamepass = table.find(self.__StorageGamepass, gamepassId)
if not findGamepass then
local err = "Gamepass with ID " .. gamepassId or 0 .. " does not exist in storage."
response._error = true
response._errorMessage = err
callback(player, gamepassId, false, nil)
for _, fn in ipairs(response._catchers) do
fn(err)
end
return
end
local success, gamepassInfo = pcall(function()
return MarketplaceService:GetProductInfo(gamepassId, Enum.InfoType.GamePass)
end)
if success and gamepassInfo then
if self.CanChangePrice then
if self.DebugMode then
warn(
self.__Prefix
or "Default" .. "Checking gamepass price for player: " .. player.Name
or "Unknown" .. " | TIME: " .. os.date("%X")
)
end
local TempData = GamepassReceipt.__TemporalDataSaved[player.UserId] or { PriceInRobux = 0 }
if not TempData or TempData == nil then
TempData = { PriceInRobux = 0 }
end
local PriceInRobux = TempData.PriceInRobux
if PriceInRobux ~= gamepassInfo.PriceInRobux then
local err = "Gamepass price has changed. Cannot proceed with purchase."
response._error = true
response._errorMessage = err
callback(player, gamepassId, false, nil)
for _, fn in ipairs(response._catchers) do
fn(err)
end
return
end
end
if self.TrackingEnabled then
self.__Profiles[player.UserId] = self.__Profiles[player.UserId] or {}
table.insert(self.__Profiles[player.UserId], {
gamepassId = gamepassId,
purchaseDate = os.date("%Y-%m-%d %H:%M:%S"),
})
end
GamepassReceipt.__TemporalDataSaved[player.UserId] = nil
callback(player, gamepassId, true, gamepassInfo)
return
else
local err = "Failed to retrieve info for Gamepass ID: " .. gamepassId
response._error = true
response._errorMessage = err
callback(player, gamepassId, false, nil)
for _, fn in ipairs(response._catchers) do
fn(err)
end
end
end)
return response
end
--//PROMPT PLAYER TO PURCHASE A GAMEPASS
function GamepassReceipt:PromptGamepassPurchase(player: Player, gamepassId: number)
local response = {
_success = false,
_error = nil,
_errorMessage = "nil",
}
function response:andCatch(callback)
if response._error then
callback(self._errorMessage)
end
return self
end
function response:andThen(callback)
if self._success then
callback()
end
return self
end
if GamepassReceipt.TrackingEnabled then
if GamepassReceipt.__Profiles[player.UserId] == nil then
response._errorMessage = "Player profile not found"
return response
end
end
-- Si ya fue guardado algo antes
if GamepassReceipt.__TemporalDataSaved[player.UserId] then
response._errorMessage = "Player already prompted or pending"
return response -- This will prevent multiple prompts for the same player
end
-- Verificación y cache temporal
if GamepassReceipt.__TemporalDataSaved[player.UserId] == nil then
GamepassReceipt.__TemporalDataSaved[player.UserId] = {}
end
-- Intentar obtener la información del producto
local success, result = pcall(function()
return MarketplaceService:GetProductInfo(gamepassId, Enum.InfoType.GamePass)
end)
if success and result then
GamepassReceipt.__TemporalDataSaved[player.UserId] = result
MarketplaceService:PromptGamePassPurchase(player, gamepassId)
response._success = true
return response
end
if success and not result then
local msg = "Gamepass with ID " .. gamepassId .. " does not exist or is invalid."
response._errorMessage = msg
return response -- This will catch any other errors that may occur
end
if not success then
local msg = "Failed to get product info for gamepass ID " .. gamepassId .. ": " .. tostring(result)
response._errorMessage = msg
return response -- This will catch any other errors that may occur
end
-- Error handling
response._errorMessage = "Unknown error"
return response
end
return GamepassReceipt